GitHub Actions Cache Key Collision Across Branches Restores Wrong Cache
A cache key that is not specific enough lets different branches share one cache entry, so a branch restores another branch stale dependencies and builds against the wrong state.
What this error means
A branch sees dependencies or build output that belong to a different branch, producing confusing failures that disappear once the cache is bypassed. The key is too generic to isolate branches.
# every branch shares this single key -> cross-branch collision
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-cacheCommon causes
Key lacks a lockfile or content hash
A static key like npm-cache never changes, so any branch writing it overwrites the shared entry and every branch restores whatever was saved last.
Caches are visible across branches by design
A cache saved on the default branch is restorable by feature branches. Without a content-based key, a generic key lets that shared scope serve a mismatched cache.
How to fix it
Key on a content hash, fall back via restore-keys
Include hashFiles of the lockfile so the key changes with dependencies, and use restore-keys for warm partial restores.
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-Scope the key when branches must not share
- Add a lockfile hash so the exact key only matches identical dependency sets.
- If branches truly need isolation, add github.ref_name to the key.
- Keep restore-keys broad enough for warm starts but rely on the hashed key for correctness.
How to prevent it
- Always include a content hash (hashFiles) in cache keys.
- Use restore-keys for warm partial restores, not as the primary key.
- Add the branch to the key only when cross-branch sharing is genuinely unsafe.