Skip to content
compiler.dev

Cache Not Found for Input Keys: Causes and Fixes

Last updated

Cache not found for input keys is a cache miss from actions/cache: no saved cache matched your key or restore-keys, so the step carries on without restoring anything. It is a warning in most workflows and the job continues, but if your next step needs the cache it can look like a failure. This page explains why the cache was not found and how to make it hit. Rules are from GitHub's dependency caching reference, checked October 2026.

The exact message

Cache not found for input keys: Linux-npm-3f1c0d9a2b..., Linux-npm-

For actions/cache/restore with fail-on-cache-miss: true you instead get a failed step. The first key listed is your key, the others are your restore-keys. A miss on the first run for a new key is normal: the post step saves the cache, and later runs hit it.

How the lookup works

  1. The action looks for an exact match on key in the current branch.
  2. If none, it looks for prefix matches on key, then each restore-keys entry, in order. With several partial matches it returns the most recently created cache.
  3. If none, it repeats the search on the default branch.

Scope rules: a run can restore caches from the current branch or the default branch, and pull requests can also restore from the base branch. It cannot restore caches from sibling or child branches. Tag caches are isolated per tag. Entries not accessed for 7 days are removed, and the default limit is 10 GB per repository.

Causes, most likely first

1. It is the first run for this key

Any new key misses once. If your key includes hashFiles('**/package-lock.json'), every lockfile change misses once. Nothing to fix. Add restore-keys so a near match restores something:

- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      npm-${{ runner.os }}-

2. The key changes on every run

Keys like ${{ github.sha }}, ${{ github.run_id }} or a timestamp never match a previous run. Key on the file that determines the content (the lockfile), not the commit. The cache key builder generates safe keys.

3. The cache lives on a branch you cannot read

A feature branch saves its cache under that branch. A different feature branch cannot see it. Pull request runs save caches for the merge ref, so one PR does not warm another. Fix: make sure the default branch saves the cache, for example with a push-to-main workflow or a scheduled run, so every branch can fall back to it.

on:
  push:
    branches: [main]
  schedule:
    - cron: "0 3 * * *"

4. The cache was evicted

After 7 days without access, or when the repository goes over 10 GB, entries are deleted, least recently used first. Check Actions, then Caches in the repository, or run gh cache list. Details and fixes in GitHub Actions cache limits.

5. The path or runner.os differs

The cache is tied to its key and also its version, derived from the paths and compression method. Changing path, or restoring on a different OS than it was saved on, produces a miss. Include runner.os in the key, and keep the path identical in save and restore.

6. The save never happened

The post step saves only if the job succeeded and the key had no exact hit. If the job failed before the end, nothing was saved. Use actions/cache/save with if: always() when you want to save after failure:

- uses: actions/cache/restore@v4
  id: cache
  with: { path: ~/.cache/pip, key: pip-${{ hashFiles('requirements.txt') }} }
- run: pip install -r requirements.txt
- uses: actions/cache/save@v4
  if: always() && steps.cache.outputs.cache-hit != 'true'
  with: { path: ~/.cache/pip, key: pip-${{ hashFiles('requirements.txt') }} }

7. Two jobs race to save the same key

Cache entries are immutable. If two matrix cells save the same key, one wins and the other logs a reserve error. Give each cell its own key or save from one job.

Use the cache-hit output

cache-hit is true only on an exact key match, not on a restore-key match. Use it to skip work:

- uses: actions/cache@v4
  id: cache
  with:
    path: node_modules
    key: nm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- if: steps.cache.outputs.cache-hit != 'true'
  run: npm ci

Remember that a restore-key hit sets cache-hit to false while still restoring files.

When caching is not helping

If caches keep missing, are huge, or restores are slower than the install they replace, a runner with warm local caches may be a better answer. compiler.dev machines keep dependency, git and Docker layer caches ready, so you do not manage keys for those; see pricing. Other options: GitHub Actions caching and Docker layer caching. Large caches also cause No space left on device.

How to prevent it

  1. Key on lockfiles, add a restore-keys fallback, and include runner.os.
  2. Save caches from the default branch so every PR can restore them.
  3. Keep total cache size well under 10 GB.
  4. Check the hit rate in your logs weekly, and use the cache hit savings estimator to see what a miss costs.

FAQ

Is "Cache not found for input keys" an error?

No, it is an informational message about a miss. The job continues unless you set fail-on-cache-miss: true.

Why do I get a cache miss on every pull request?

PR caches are scoped to the merge ref, and a PR can only restore from itself, its base branch and the default branch. Make sure the default branch saves the cache.

How long does a GitHub Actions cache last?

Seven days since the last access, unless evicted earlier for exceeding the repository limit.

Why does my cache restore but cache-hit is false?

A restore-key (prefix) match restores files but does not count as an exact hit, so cache-hit is false.