Report #17223
[bug\_fix] Cache not found or 100% cache miss rate despite successful cache uploads in previous runs
Remove dynamic values like \`github.sha\`, \`github.run\_id\`, or timestamps from the cache key. Instead, use stable identifiers such as lockfile hashes \(e.g., \`hashFiles\('\*\*/package-lock.json'\)\`\), operating system \(\`runner.os\`\), or static toolchain versions. Additionally, implement \`restore-keys\` with partial prefixes \(e.g., \`$\{\{ runner.os \}\}-node-\`\) to enable fallback to older caches when exact matches aren't found. The root cause is that cache keys must match exactly; dynamic keys that change every run create cache entries that can never be retrieved.
Journey Context:
A developer implements caching for their Node.js dependencies using \`actions/cache\`, following a tutorial. They set the key to \`$\{\{ runner.os \}\}-node-$\{\{ github.sha \}\}\` thinking that tying the cache to the specific commit would ensure freshness. After the first run uploads a 2GB cache, every subsequent run reports 'Cache not found' and spends 5 minutes reinstalling node\_modules. The developer checks the Actions tab, sees 'Post Cache' step uploading a new cache every time with a unique SHA-based key. Searching for 'github actions cache key best practices', they find documentation explaining that the \`key\` is the lookup value, not a metadata tag. They realize the SHA changes every commit, guaranteeing a miss. They refactor the key to \`$\{\{ runner.os \}\}-node-$\{\{ hashFiles\('\*\*/package-lock.json'\) \}\}\` and add \`restore-keys: $\{\{ runner.os \}\}-node-\` as a fallback. The next run misses the exact key but restores from the prefix key, then uploads a new cache with the updated lockfile hash. Subsequent runs with unchanged dependencies hit the exact key and complete in 30 seconds.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T04:48:43.237624+00:00— report_created — created