# Cache service responded with 503 or 429 in GitHub Actions

> Fix cache service responded with 503 and 429 in GitHub Actions: keep a cache blip from failing the build, and tell a real outage from your own load.

Source: https://latchkey.dev/learn/failures/github-actions-cache-service-503-429  
Updated: 2026-09-20

Cache service responded with 503 in GitHub Actions is the Actions cache backend refusing one request, and a 429 is the same backend throttling you. Neither is a problem with your key: the step warns, the job carries on without a cache, and the only thing that turns it into a red build is a workflow that treats a cache miss as fatal.

## What this error means

A cache step logs a warning rather than an error, and the job keeps going: "Warning: Failed to restore: Cache service responded with 503". A 429 appears in the same shape and means the opposite thing, that the service is working and has decided you are asking too often. Either way the restore did not happen, so the steps after it run cold: the install downloads everything, the build has no incremental state, and the job takes the long route. The save at the end of the job can fail the same way, which is worse in one specific sense, because the next run then starts cold too. The block below is a sample from the pattern library rather than from a run of ours, and the section on reproduction explains why there is no run on this page.

```Sample log from the pattern library
Warning: getCacheEntry failed: Cache service responded with 503
```

## Common causes

### The cache service had a bad few minutes

The ordinary case. A 503 is the backend declining one request during an incident, a deploy or a load spike, and it clears on its own. Nothing in the workflow caused it and nothing in the workflow will prevent it; the only useful response is to make sure it costs a slower build rather than a failed one.

### You set continue-on-error and the job still failed

The wasted fix, because the tolerance went on the step that was already tolerant. The cache step warns; the failure is in whatever ran next on the assumption that the cache was restored. Look for a step gated on the cache-hit output, a `fail-on-cache-miss`, or a test command that expects a directory the restore was supposed to create.

### Your own concurrency earned the 429

A wide matrix, several workflows triggered by one push, or a monorepo running twelve jobs that each save a large cache will produce a burst of cache traffic in a short window. The service answers that with a throttle. It is transient, and it is also a design signal: the same caches are being written many times over.

### The caches are large enough to make every operation fragile

A multi-gigabyte cache takes a long time to upload and download, which widens the window in which something can go wrong, and it pushes other entries out under the eviction policy. In our experience the repositories that see cache-service errors most often are the ones caching a built tree rather than a package store.

## How to fix it

### Let a cache miss be a cache miss

1. Remove `fail-on-cache-miss` unless the job genuinely cannot run without the cache.
2. Make sure the install step runs whether or not the restore succeeded, rather than being skipped on a cache hit.
3. Re-run the workflow once the service recovers, and expect the first run after the incident to be slow.

```.github/workflows/ci.yml
- uses: actions/cache@v6
  id: cache
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('package-lock.json') }}
- run: npm ci --no-audit --no-fund
```

### Put the tolerance where the failure is

If something downstream really does need the cache, guard that step rather than making the whole job depend on a service you do not control. A conditional fallback is a few lines and it turns an outage into a slow build.

```.github/workflows/ci.yml
- uses: actions/cache@v6
  id: deps
  with:
    path: ~/.cache/pip
    key: pip-${{ hashFiles('requirements.txt') }}
- if: steps.deps.outputs.cache-hit != 'true'
  run: pip install -r requirements.txt
```

### Reduce the cache traffic you generate

Cap matrix parallelism, share one warm cache across legs with `restore-keys`, and cache the package store instead of the installed tree. Three smaller entries that restore reliably beat one large entry that is evicted on Tuesday.

```.github/workflows/ci.yml
strategy:
  max-parallel: 4
  matrix:
    node: [20, 22, 24]
```

### Check whether it is you or them

Before changing the workflow, look at the status page and at how many cache operations the run made. A single 503 in one job during an incident needs no change at all. The same warning in every job of a wide matrix, repeatedly, is your own load and the fix is above.

## How to prevent it

- Never gate a build on a successful cache restore unless it truly cannot proceed.
- Cache the dependency store rather than the installed tree, so entries stay small.
- Cap matrix parallelism when every leg saves its own cache.
- Keep an eye on the 10 GB repository limit, because eviction takes the least recently used entries first.

## A failed restore is a warning, by design

The cache action is an optimisation and it behaves like one. GitHub documents the same posture for the access-mode case: when a cache operation is skipped, "the action logs an informational message and the step and run continue without failing", and a read-only run that tries to save gets a failed save while "the step and the job do not" fail.

So a 503 on its own cannot turn your build red. If your build did go red, something downstream insisted: `fail-on-cache-miss`, a step guarded on `cache-hit`, or an install step that assumed the dependencies were already there. That is the thing to find, and it is usually three lines below the warning.

```Terminal
grep -n "cache-hit\|fail-on-cache-miss" .github/workflows/*.yml
```

## A 429 is usually your own matrix

A 503 is the service having a bad minute and is nobody's fault. A 429 is a rate limit, and rate limits are a response to a request rate, so the first place to look is the shape of your own workflow. A twenty-leg matrix that each saves its own multi-gigabyte cache at roughly the same second is a burst by any measure.

Two changes cut it without giving up caching. Cap how many legs run at once, and let the legs share one warmed cache through `restore-keys` instead of each writing its own.

```.github/workflows/ci.yml
strategy:
  max-parallel: 4
  matrix:
    python: ['3.11', '3.12', '3.13']
steps:
  - uses: actions/cache@v6
    with:
      path: ~/.cache/pip
      key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
      restore-keys: |
        pip-${{ runner.os }}-
```

## Size and eviction, so you know what you are asking for

GitHub documents the default limit as 10 GB per repository, and says it "can be increased by enterprise owners, organization owners, or repository administrators". It also removes "any cache entries that have not been accessed in over 7 days", and when the limit is reached it deletes "in order of last access date, from oldest to most recent".

Those two rules explain most "the cache was there yesterday" reports, and they also explain why a repository that caches everything ends up caching nothing useful: a handful of large entries push out the small ones that were doing the work.

## What the runner detects, and why there is no run on this page

Latchkey carries `GHA_CACHE_RESTORE_FAIL` at confidence 0.86, matching the getCacheEntry wording and the cache-service status line, and its plan is a short retry: two attempts starting at 5 seconds. The library files it as a documented transient and notes the same asymmetry this page opens with, that "the cache step itself is non-fatal but a downstream step expecting cached deps will fail".

Every other failure page in this cluster carries a recorded run. This one does not, and the reason is worth stating plainly: the Actions cache service answers requests from inside a GitHub Actions job and nowhere else, so there is no way to reproduce a 503 from it on a Latchkey runner without fabricating one. A stub returning 503 would produce a screenshot, not evidence. The sample above is the pattern library's, and this page makes no claim about a repair we have not run.

## FAQ

### Is "Cache service responded with 503" a build failure?

Not on its own. The cache action logs a warning and the job continues without the cache, so a build that went red needs a second reason: a step gated on the cache-hit output, `fail-on-cache-miss`, or a command that assumed the restored files were there. Find that step, because it is the one you can fix.

### Why does the Actions cache throttle a matrix build?

Because a wide matrix concentrates a lot of cache traffic into a few seconds: every leg restores at the start and saves at the end, at roughly the same time. The service answers a burst with a 429. Capping `max-parallel` and sharing one warm cache through `restore-keys` removes most of it.

### How large can a GitHub Actions cache be?

GitHub documents a default of 10 GB per repository, which enterprise owners, organization owners or repository administrators can raise. Entries not accessed in more than 7 days are removed, and when the limit is reached the oldest by last access go first, so a few large caches quietly evict the small ones.

### Should I re-run a workflow when the cache service returns 503?

Only if something downstream turned the warning into a failure, and then the better answer is to fix that step. If the job merely ran cold, the result is valid and slower. Re-running during an ongoing incident tends to produce the same warning and another slow run.

## References

- [GitHub Actions: dependency caching reference](https://docs.github.com/en/actions/reference/dependency-caching-reference)
- [actions/cache](https://github.com/actions/cache)
- [actions/toolkit#725: transient cache service responses](https://github.com/actions/toolkit/issues/725)
- [GitHub status history](https://www.githubstatus.com/)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
