fix: return 404 for stale cache entries with missing storage objects#223
Merged
LouisHaftmann merged 1 commit intofalcondev-oss:devfrom Apr 14, 2026
Merged
fix: return 404 for stale cache entries with missing storage objects#223LouisHaftmann merged 1 commit intofalcondev-oss:devfrom
LouisHaftmann merged 1 commit intofalcondev-oss:devfrom
Conversation
When a cache entry exists in the database but the underlying storage object has been lost (incomplete upload, manual deletion, lifecycle policy), the download route previously threw and returned 500, causing BuildKit clients to hang instead of falling back to the upstream registry. Storage.download() now pre-validates part presence via countFilesInFolder before committing to an HTTP response, and adapters throw a typed ObjectNotFoundError when the merged blob is missing. Both are caught in the outer try/catch, which logs a warning and returns undefined so the route returns 404. Stale rows are left for the existing cleanup task to reap, avoiding mutating DB state on a read path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #222
Problem
When a cache entry exists in the DB but its backing storage object has been lost (interrupted upload, manual deletion, lifecycle rule, partial bucket wipe),
GET /download/:cacheEntryIdthrew an unhandled error and returned 500. BuildKit's cache-import client retries on 500 and hangs the workflow instead of falling back to the upstream registry.Solution
Return 404 when the storage object is missing so clients treat it as a clean cache miss.
The download path has three branches based on
storage_locations.mergedAt/mergeStartedAt:adapter.createDownloadStream(merged)already awaits a synchronous existence check in each adapter, so errors surface before any response bytes are written. The outertry/catchjust needs to recognize them.countFilesInFolderbefore returning the response stream.When a stale entry is detected, the server logs a warning and returns
undefined; the route yields 404; the existing cleanup task reaps the row on its normal schedule. No DB mutation on the read path.Changes
ObjectNotFoundErrorraised by each adapter'screateDownloadStreamwhen the object is missing.Storage.ensurePartsExist(location)— usescountFilesInFolderto pre-probe before committing to an HTTP response.Storage.download()wraps the body in a singletry/catchthat convertsObjectNotFoundErrorintoundefined(→ 404) and rethrows everything else.FileSystemAdapter.countFilesInFoldernow returns0for a missing folder instead of throwingENOENT, matching S3/GCS list-by-prefix semantics.try/catcharound the merge-upload promise setup (uploadStreamisasync, so a sync throw isn't possible).Testing
pnpm run type-check✓pnpm run lint✓pnpm run test:run✓ (new tests pass under filesystem + sqlite locally; full 3×3 matrix runs in CI)New
tests/stale-cache.test.tscovers both parts-streaming paths by wiping storage throughStorage.getAdapterFromEnv()so it runs under all three storage drivers in CI:Representative server log from a run confirming the fix:
```
[warn] Stale cache entry 4f31d42e-...: Object not found in storage: 1550524356/merged
[error] Response: GET /download/4f31d42e-... > 404
```
Notes