Actions cache miss every run - slow builds from scratch
Build step duration doubled - dependencies reinstalled every run
Focused incident mode — strips away everything but this runbook.
Symptoms
- Build step duration doubled - dependencies reinstalled every run
- actions/cache step logs 'Cache not found for key: <key>'
- Package manager installs node_modules or pip from scratch each time
Actions cache keys are exact-match by default. A key with github.sha or github.run_id will miss every time. Use hashFiles on lockfiles for reliable cache hits.
Evidence to grab
Collect these artifacts before you change anything — they prove (or rule out) each cause.
Cache restore log output
gh run view <RUN_ID> --log | grep -A3 'actions/cache'Cache key configuration
cat .github/workflows/<file>.yml | grep -A5 'key:'
Diagnostic flow
Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.
Guided decision tree is ready.
Steps with commands, expected output, and pass/fail branches.
Ranked root causes
Evidence checklist
- key: ${{ runner.os }}-npm-${{ github.run_id }}
- Cache is unique per run and never matches any previous run
Confirm before fixing
cat .github/workflows/<file>.yml | grep 'key:' | head -3Read the cache key
Fix
sed -i 's/${{ github.run_id }}/${{ hashFiles("**\/package-lock.json") }}/' .github/workflows/<file>.ymlReplace run_id with a file-hash-based key
Resolution runbook
Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.
gh run view <RUN_ID> --log | grep -A10 'Cache'Read the cache restore output
cat .github/workflows/<file>.yml | grep -B2 -A15 'key:'Inspect the key and restore-keys
gh cache list --repo <OWNER/REPO>List existing caches to see what keys exist
gh cache delete <CACHE_KEY> --repo <OWNER/REPO>Delete stale caches if needed
Prevention
- Key on hashFiles of the lockfile (package-lock.json, go.sum, requirements.txt), never on run_id or sha.
- Always provide restore-keys with a broad prefix for fallback matching.
- Keep cache paths consistent with the package manager's actual cache directory.
- Set a reasonable cache-size limit (default 10 GB per repo) to avoid eviction.
Related runbooks
Same stack, same failure family — worth a skim.
Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.