Repository navigation
fix(filesv2): end upload requests that never answer instead of freezing the batch - #1257
Merged
Merged
Conversation
…ng the batch The Filegate SDK has no request deadline (k2b-dev/filegate#5). A status GET, segment-page GET or segment PUT that never answered froze the whole upload batch: the bar stood still, the row stayed "working", and no message appeared. The CLI sat silent in the same way, and target resolution and folder creation for put --parents had no deadline either. transferUpload now hands DirectSession a Cloud-owned request function with a deadline per attempt: 30 s for control requests, 60 s plus the body at 64 KiB/s (about 0.5 Mbit/s) for segment PUTs. A browser fetch cannot observe request-body bytes, so a size-proportional deadline is the honest substitute for an inactivity timer. The deadline also covers the response body. When it expires, the request function aborts the signal the SDK upload runs under, so the SDK stops instead of retrying the same hung request up to three times (about 9.4 min for an 8 MiB segment). The SDK keeps its quick retries for network errors and short Retry-After answers. A caller cancel keeps its own reason. Failures become one UploadTransferError with a kind (stalled, network, busy, final) and the Retry-After hint; the Files workspace and public inbox show "No response from the file server" or "The file server is busy" in English and German, and the batch continues with the next file. Folder preparation uses the same 60 s control deadline. The CLI gives target resolution and folder creation a 60 s deadline, maps transfer failures to plain localized reasons without lease URLs, and writes phase and acknowledged-byte progress to stderr in text mode: one updating line at most once per second on a terminal, phase lines otherwise. Standard output and --json stay unchanged. Guard: tests keep every new DirectSession( inside upload-transfer.ts, expect one attempt per expired deadline, and an integration test against a real Filegate checks that a hung second segment ends as "stalled" with the session still open and segment 0 kept. Part of #1163
… body breaks The Cloud request function buffers each Filegate response. When a 429 with a long Retry-After lost its connection while the body was read, the read error reached the SDK as a transport failure, and the SDK retried after 250 ms instead of honoring Retry-After. A non-success response whose body cannot be read now keeps its status and headers, so the SDK's own status handling applies. In the CLI, readJson turns an unreadable non-success body into a status error, so a deadline that stalled a 503 body showed "503 Service Unavailable" instead of the localized deadline message. readWithin now checks its signal when reading fails. Guard: transfer tests for a 429 and a 503 with a broken body, and the CLI deadline tests gain a stalled 503 body for every request.
…file server errors final A Cloud request that hits its deadline in the browser said the file server did not answer, so the row blamed the wrong component. It now says the server did not answer. Any Filegate 5xx was called busy, which invited a retry that fails the same way for 500 or 501; busy now means only the transient 408, 429, 502, 503 and 504. On a terminal, a long file name could wrap the one updating progress line and leave fragments; the name is shortened to fit the terminal width.
… in the CLI reference The CLI reference did not say which file server answers count as busy or that a long file name is shortened on a terminal, so readers could not tell a retryable failure from a final one.
ValentinKolb
enabled auto-merge
October 16, 2026 15:33
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Oct 16, 2026
…meouts # Conflicts: # docs-site/docs/en/contributing/lessons-learned.md
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.
Part of #1163. Upstream: k2b-dev/filegate#5 (the Filegate SDK has no request deadline; Filegate itself is unchanged).
Outcome
No upload request can wait forever any more: Files workspace, public inbox, folder preparation and
cld filesv2 put.cld filesv2 put: target resolution,--parentsfolder creation (alsomkdir --parents), file-ID resolution, session opening and commit have a 60 s deadline, also when the response body stalls after its headers (including a non-success status). Transfer failures map to plain localized reasons without lease URLs. In text mode, progress goes to stderr: on a terminal one updating line (acknowledged bytes and percent, at most once per second, with a long file name shortened to the terminal width), otherwise only phase lines (uploading, finishing, done). Stdout and--json/--jsonlare unchanged.Root cause
DirectSession(Filegate SDK 7.0.0) callsfetchwithout a deadline, and Cloud passed none. A status GET, segment-page GET or segment PUT that never answered left the row "working" with no message, and the queue waited behind it. A plain per-request timeout is not enough: the SDK retries a thrown fetch error unless its own signal is aborted.Design
transferUploadpasses a Cloud-owned request function tonew DirectSession(url, request). Deadlines: 30 s for control calls; 60 s plus body size at 64 KiB/s (about 0.5 Mbit/s) for segment PUTs. A browser fetch cannot observe request-body bytes, so a true inactivity timer is impossible; the size-proportional deadline is the honest substitute and is named a deadline in code and docs.TypeErrors and for 429/502/503/504 with a short Retry-After. A caller cancel keeps its own reason and is never reported as a stall.UploadTransferErrorcarries a kind (stalled,network,busyfor 408/429/502/503/504,finalfor everything else) and the Retry-After hint (seconds or HTTP date).new DirectSession(insideupload-transfer.ts.uploadFilethere is no SIGINT handler, so Ctrl-C still ends the process during resolution and folder creation; those requests only needed the deadline. During the transfer, Ctrl-C keeps aborting the session as before.browser-upload.behavior.test.tsxflushes leftover work inafterEach: without it, a fire-and-forgetabortfrom the previous test leaked into "a folder request with no response fails its group visibly" (reproduced).How do we prevent this next time?
Guards:
upload-transfer.test.tsexpects exactly one attempt per expired deadline (status, segments, PUT, stalled response body), keeps Retry-After when an error body breaks, checks that caller cancel stays distinct, and classifies 500/501 as final after one attempt.upload-messages.test.tsmaps a CloudTimeoutErrorto "No response from the server" and a stalled transfer to "No response from the file server". A CLI test keeps every TTY progress line within the terminal width.filegate-transfer.integration.test.tsruns a real session against the test Filegate with a hung second segment PUT and expectsstalledwithin the shrunken deadline, one hung attempt, and the session still open with segment 0 received (on main the call never settles). CLI tests stall resolution,put --parents,mkdir --parents, file-ID resolution and session opening with headers that never come, a body that stalls, and a 503 whose body stalls, and expect the localized deadline error; on main they hang. The construction test keepsDirectSessionbehind the deadline. The lesson is recorded on the lessons-learned page.Docs
docs-site/apps-content/en/filesv2.md: deadline sized for the slowest supported uplink (about 0.5 Mbit/s); CLI progress on stderr.docs-site/apps-content/en/filesv2.mdalso names the two browser deadline messages and the busy statuses.packages/filesv2/src/cli-references/index.md: deadlines, messages, busy statuses (408, 429, 502, 503, 504; other failures final), progress on stderr, name shortened to the terminal width.filesv2-start:putshows progress; a file that gets no answer fails with a reason and the upload continues.docs-site/docs/en/contributing/lessons-learned.md: new lesson and silent-failure guard row.Verification
All on the final HEAD, rebased on current
main.bun run --cwd packages/filesv2 typecheck: passed.bun run test --filter packages/filesv2(Chromium): unit 382 passed (109 integration-only skipped), render 5 passed, behavior 113 passed.TEST_BROWSER=webkitagainst a Playwright 1.63.0 WebKit server):bun run test --browser --filter packages/filesv25 files, 63 tests passed; behavior 113 passed. In the plainbun run testrun (browser files sharing one process on a loaded machine), the unchangedStorageOverview.browser.test.tstimed out on its first test once (5 s); alone it passed 3 of 3 runs. This change does not touch it.bun run test --integration --filter packages/filesv2against the test infrastructure (incl. test Filegate): 519 passed, 0 failed.upload-transfer.test.ts; reverting theTimeoutErrormapping failsupload-messages.test.ts; disabling the name truncation fails the TTY width test incli-upload.test.ts. The new CLI deadline tests hang on main until the test timeout.bun scripts/check.ts help-writing localization biome boundaries module-mocks: 5/5 passed.git diff --check origin/main...HEAD: clean.harper-cliis not installed on this machine, so the changed English Markdown had no Harper lint.Operations
No configuration, migration or deployment change. Uploads on very slow links (below about 0.5 Mbit/s) may now fail with "No response from the file server" instead of hanging.