Store queued upload files in Cache Storage to fix web InvalidBlob#96493
Draft
elirangoshen wants to merge 1 commit into
Draft
Store queued upload files in Cache Storage to fix web InvalidBlob#96493elirangoshen wants to merge 1 commit into
elirangoshen wants to merge 1 commit into
Conversation
elirangoshen
force-pushed
the
eliran/2397-web-upload-cache-storage
branch
3 times, most recently
from
July 20, 2026 15:48
37dffd6 to
bb61bd5
Compare
Contributor
Author
|
@codex review |
elirangoshen
force-pushed
the
eliran/2397-web-upload-cache-storage
branch
from
July 21, 2026 09:24
bb61bd5 to
963b4de
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bb61bd531b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
Queuing a receipt upload for the offline queue on web wrote the file bytes inline into the single networkRequestQueue IndexedDB record. As file requests accumulated the record exceeded Chromium's IDB blob-externalization limit and the write failed with `DataError: Failed to write blobs`, deadlocking the queue so it could never drain (a self-reinforcing retry + log storm). Take the bytes out of the queue: on enqueue each inline File/Blob is written to a dedicated file store and replaced with a small QueuedFileRef; at send time the ref is resolved back into the bytes. The queue record stays tiny and queued uploads survive a browser restart. The store is backed by the Cache Storage API, which uses a different Chromium disk backend than IDB blob externalization (avoiding the failing write path) and mirrors Attachment/index.ts, which already caches receipt-class binaries there in prod. Native keeps a filesystem uri and uses a no-op stub. storeFile logs on failure for prod telemetry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
elirangoshen
force-pushed
the
eliran/2397-web-upload-cache-storage
branch
from
July 21, 2026 09:33
963b4de to
f63be9f
Compare
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.
Explanation of Change
This fixes the web root cause behind the
DataError: Failed to write blobs(InvalidBlob) storm tracked incallstack-internal/expensify-issues#2397.When a receipt file-upload is queued for the offline request queue on web, the file bytes sat inline in the single
networkRequestQueueIndexedDB record. As file requests accumulated, that record exceeded Chromium's IndexedDB blob-externalization limit and the write failed — deadlocking the queue so it could never drain. The result was a self-reinforcing retry + log storm, observed on Windows/Chromium clients with queues thousands of requests deep.This PR takes the file bytes out of the queue entirely. On enqueue, each inline
File/Blobis written to a dedicated file store and replaced in the request with a smallQueuedFileRef; at send time the ref is resolved back into the bytes. This keeps the queue record tiny and lets a queued upload survive a browser restart.The file store is backed by the Cache Storage API. Cache Storage uses a different Chromium disk backend than IndexedDB blob externalization, so it avoids the failing write path, and it mirrors
src/libs/actions/Attachment/index.ts, which already caches receipt-class binaries (same file sizes and types) in Cache Storage in production. Native is unaffected — it keeps a filesystem uri, and theQueuedFileStoragenative stub is a no-op.Changes:
src/libs/QueuedFileStorage/— new platform-split module. Web (index.ts) stores each blob in Cache Storage (caches.open/put/match/delete/keys) under a synthetic same-origin URL, preserving MIME type via aContent-Typeheader;storeFilelogs viaLog.alerton failure so prod telemetry surfaces the failure rate. Native (index.native.ts) is a no-op stub.src/libs/actions/PersistedRequests.ts— swap inline files for aQueuedFileRefon enqueue; purge the store onOnyx.clear()(logout/reset) and delete a request's stored file on terminal outcomes; startup orphan-sweep.src/libs/prepareRequestPayload/index.ts— resolve aQueuedFileRefback into its blob when building the upload; if the file is gone, send without it for a definitive outcome.tests/unit/PersistedRequests.ts— added minimal in-memory Cache Storage + Response mocks (jsdom provides neither); 17 tests pass.Validation done locally:
npm run fmt,npm run lint-changed(exit 0),npm run typecheck-tsgo(0 errors), unit tests 17/17 pass.Fixed Issues
$#87844
$#87871
PROPOSAL:
Tests
Failed to write blobs/ InvalidBlob errors appear in the JS console.Offline tests
Failed to write blobs/ InvalidBlob errors.QA Steps
Same as Tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari