Describe the bug
anthropics/claude-code-action@v1's post-job cache save fails consistently on successive runs against the same PR ref, burning ~20-30 seconds per failure on 5 retries of what
is actually a permanent conflict (the GitHub cache API's "key already exists on this ref" response). The underlying @actions/cache save logic treats the HTML-formatted error
as transient and retries with exponential backoff when it should recognize the conflict as non-retryable and skip.
To Reproduce
- Use anthropics/claude-code-action@v1 in a workflow triggered on pull_request with synchronize in the event list (default pattern).
- Open a PR that touches files the workflow processes. First run completes and successfully saves a cache entry (the 35 MB Bun binary tarball) against refs/pull//merge
with a deterministic key like bun-swrvKXH4hkDYFKcrNV+Kct676IY=.
- Push a second commit to the same PR. A new workflow run starts.
- The post-step cache save fails with the 5-retry HTML error pattern:
Post job cleanup.
/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work// --files-from manifest.txt --use-compress-program zstdmt
Sent 35242126 of 35242126 (100.0%), 58.0 MBs/sec
Attempt 1 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 3000 ms...
Attempt 2 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 5276 ms...
Attempt 3 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 9718 ms...
Attempt 4 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 10670 ms...
Warning: Failed to save: Failed to FinalizeCacheEntryUpload: Failed to make request after 5 attempts: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Expected behavior
One of:
- The cache save recognizes that the key already exists on the ref, logs something like "cache already exists for key+ref, skipping save", and exits cleanly without retrying.
- The action uses a run-unique cache key with a restoreKeys prefix fallback so each run successfully saves its own entry (standard @actions/cache pattern for content that
doesn't change across runs).
- At minimum, the 409/HTML response is treated as a non-retryable error so the 5-retry wallclock burn doesn't happen.
Either way, the warning should not appear on every second-and-subsequent push to a PR for a repo running the action as recommended.
Screenshots
N/A. Log output and cache-entry inspection in Additional context.
Workflow yml file
name: Adversarial Security Review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- 'docs/'
- 'tasks/'
- 'PROJECT-PLAN.md'
permissions: {}
jobs:
adversarial-review:
if: >
!github.event.pull_request.draft &&
github.event.pull_request.user.login != 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Adversarial Security Review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
(~50-line adversarial reviewer prompt; full text at
.github/adversarial-reviewer-prompt.md in the repo.
Prompt text is not relevant to the bug; the cache
failure fires regardless of prompt content.)
API Provider
[- ] Anthropic First-Party API (default)
[- ] AWS Bedrock
[- ] GCP Vertex
Additional context
Additional context
Cache-entry inspection confirms the key+ref collision pattern. Running gh api /repos///actions/caches on a repo exhibiting this bug shows one 35 MB Bun cache
entry per ref:
35242126 bytes | ref=refs/pull/117/merge | key=bun-swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-23T14:25:02
35242123 bytes | ref=refs/pull/109/merge | key=bun-swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-22T15:57:25
35242125 bytes | ref=refs/pull/91/merge | key=bun-swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-21T13:52:22
35242125 bytes | ref=refs/heads/main | key=swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-20T18:58:16
Each ref has exactly one cache entry at the pinned 35 MB Bun binary size. The last_accessed_at stamp is the first successful save; all subsequent saves on the same ref fail
with the retry pattern because the key is deterministic (Bun version is pinned, so the hash is stable).
Root cause. GitHub's cache API enforces "one save per key+ref" and returns a 409-shaped response (rendered as HTML at the client's level) when the key already exists. The
@actions/cache toolkit's default retry behavior treats the HTML parse failure as a transient error and burns 5 retry cycles before surfacing the warning. The bug surfaces on
every second-and-subsequent push to a PR because the bundled Bun binary cache key is stable across runs of the same action version.
Impact. Roughly 20-30 seconds of wallclock time wasted per failed save. For a repo running the action on every PR push, this accumulates to tens of minutes per month of
Actions quota burned on retry backoff. It also creates persistent warning noise in CI logs, which can mask real cache-related issues.
Proposed fixes (any of, or combination).
- Use a key with a run-unique suffix plus restoreKeys fallback to the deterministic bun-version hash. Standard @actions/cache pattern. Example: key: bun--${{
github.run_id }}, restore-keys: bun--. Each run saves its own entry; restore falls back to the most recent matching prefix. Best long-term fix.
- Catch the 409/HTML response explicitly in the save step and log as "cache already exists for key+ref, skipping save" without retrying. Smallest-diff fix. Preserves current
caching semantics; just stops the retry burn.
- Expose an input to disable caching (save_cache: false or similar) for users who want to opt out entirely. Useful escape hatch independent of (1) and (2).
- Bundle a newer @actions/cache client if upstream already handles 409 better in a recent release. Worth checking actions/toolkit history.
Environment.
- Action version: anthropics/claude-code-action@v1 (resolves to v1.0.104 as of 2026-04-23)
- Runner: ubuntu-latest
- Trigger: pull_request with types: [opened, synchronize, reopened]
- Repo visibility: private (should not be relevant)
Happy to contribute a PR for fix (1) or (2) if helpful. Needed guidance on which approach maintainers prefer.
Describe the bug
anthropics/claude-code-action@v1's post-job cache save fails consistently on successive runs against the same PR ref, burning ~20-30 seconds per failure on 5 retries of what
is actually a permanent conflict (the GitHub cache API's "key already exists on this ref" response). The underlying @actions/cache save logic treats the HTML-formatted error
as transient and retries with exponential backoff when it should recognize the conflict as non-retryable and skip.
To Reproduce
with a deterministic key like bun-swrvKXH4hkDYFKcrNV+Kct676IY=.
Post job cleanup.
/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work// --files-from manifest.txt --use-compress-program zstdmt
Sent 35242126 of 35242126 (100.0%), 58.0 MBs/sec
Attempt 1 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 3000 ms...
Attempt 2 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 5276 ms...
Attempt 3 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 9718 ms...
Attempt 4 of 5 failed with error: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Retrying request in 10670 ms...
Warning: Failed to save: Failed to FinalizeCacheEntryUpload: Failed to make request after 5 attempts: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Expected behavior
One of:
doesn't change across runs).
Either way, the warning should not appear on every second-and-subsequent push to a PR for a repo running the action as recommended.
Screenshots
N/A. Log output and cache-entry inspection in Additional context.
Workflow yml file
name: Adversarial Security Review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- 'docs/'
- 'tasks/'
- 'PROJECT-PLAN.md'
permissions: {}
jobs:
adversarial-review:
if: >
!github.event.pull_request.draft &&
github.event.pull_request.user.login != 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
API Provider
[- ] Anthropic First-Party API (default)
[- ] AWS Bedrock
[- ] GCP Vertex
Additional context
Additional context
Cache-entry inspection confirms the key+ref collision pattern. Running gh api /repos///actions/caches on a repo exhibiting this bug shows one 35 MB Bun cache
entry per ref:
35242126 bytes | ref=refs/pull/117/merge | key=bun-swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-23T14:25:02
35242123 bytes | ref=refs/pull/109/merge | key=bun-swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-22T15:57:25
35242125 bytes | ref=refs/pull/91/merge | key=bun-swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-21T13:52:22
35242125 bytes | ref=refs/heads/main | key=swrvKXH4hkDYFKcrNV+Kct676IY= | last=2026-04-20T18:58:16
Each ref has exactly one cache entry at the pinned 35 MB Bun binary size. The last_accessed_at stamp is the first successful save; all subsequent saves on the same ref fail
with the retry pattern because the key is deterministic (Bun version is pinned, so the hash is stable).
Root cause. GitHub's cache API enforces "one save per key+ref" and returns a 409-shaped response (rendered as HTML at the client's level) when the key already exists. The
@actions/cache toolkit's default retry behavior treats the HTML parse failure as a transient error and burns 5 retry cycles before surfacing the warning. The bug surfaces on
every second-and-subsequent push to a PR because the bundled Bun binary cache key is stable across runs of the same action version.
Impact. Roughly 20-30 seconds of wallclock time wasted per failed save. For a repo running the action on every PR push, this accumulates to tens of minutes per month of
Actions quota burned on retry backoff. It also creates persistent warning noise in CI logs, which can mask real cache-related issues.
Proposed fixes (any of, or combination).
github.run_id }}, restore-keys: bun--. Each run saves its own entry; restore falls back to the most recent matching prefix. Best long-term fix.
caching semantics; just stops the retry burn.
Environment.
Happy to contribute a PR for fix (1) or (2) if helpful. Needed guidance on which approach maintainers prefer.