fix(api): upload empty files with an explicit zero length - #167
Merged
Merged
Conversation
requests decides a body's length with a truth test, so a zero-byte file is indistinguishable from a body whose length it cannot determine and falls back to "Transfer-Encoding: chunked". Presigned storage PUTs reject chunked bodies with 501, which the uploader treats as transient and retries ten times with exponential backoff — minutes of apparent hang per empty file, then a failure. Send the empty body directly so requests takes its no-body path and sets "Content-Length: 0". Also extract the PUT status handling so both the empty and streaming paths share it.
codexceed
requested review from
ethanwharris,
justusschock,
k223kim,
rusenask and
tejapulagam
as code owners
September 18, 2026 15:12
codexceed
commented
Sep 18, 2026
added 3 commits
September 18, 2026 17:08
Review follow-ups on the empty-file upload fix: - Build the request body once and issue one PUT with one status check, instead of an early return with its own call site. The progress bar is now created for empty files too, which the early return had skipped. - Stop classifying 501 and 505 as transient. They reject the request itself, so retrying only burns the ten-attempt ladder before failing anyway. - Fold the new tests into one parametrized length assertion, and cover the empty body in the existing no-retry test rather than duplicating it.
The CLI turns an unhandled error into a titled panel with a LIGHTNING_DEBUG hint, but only because the error travels out of Click's main() to the interpreter's excepthook. Existing tests call the formatter and the hook directly, so nothing covered that last link, and removing the hook's installation left the suite green while every command started printing raw tracebacks. Add coverage for the link itself: that running a command installs the hook, that Click lets a plain error escape, and end to end in a real interpreter that a failing command prints the panel rather than a traceback - and still prints the traceback under LIGHTNING_DEBUG=1. The end-to-end driver runs from a temp directory, so it pins PYTHONPATH to the package pytest imported; otherwise an installed copy shadows the checkout and the test silently passes against the wrong code.
…-chunked-encoding
owbone
approved these changes
Sep 21, 2026
ethanwharris
approved these changes
Sep 21, 2026
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.
Description
Uploading an empty file to Lightning storage always failed, and failed slowly. Empty
__init__.pyfiles are normal in Python packages, so this hit most projects on their first upload or deploy.
It affects everything that uploads:
lightning cp(single file and-r), teamspace and studiouploads, and deployment export. They all share one uploader.
Motivation
An empty file went out without declaring its size. Storage refuses those. The uploader then read
that refusal as a temporary glitch and kept retrying something that could never work.
flowchart LR A[Empty file] --> B[Sent without<br/>declaring a size] B --> C[Storage refuses it] C --> D[Refusal mistaken for<br/>a passing glitch] D --> E[Retried 10 times,<br/>up to 8 minutes] E --> F[Fails anyway]From the user's side this looked like a hang with no explanation, for an unpredictable number of
minutes, before the upload gave up. A folder upload died on the first empty file it reached.
Changes
Progress bars are unchanged: a folder upload still prints one per file, empty ones included.
Testing
checked the arguments handed to a stand-in, which is why they passed on empty files while the
real thing was broken. Undoing the fix fails them.
the CLI, which prints an error panel rather than a traceback. That was untested, so this PR also
covers it: that a failing command reaches the handler, and end to end that it prints the panel
and still gives a traceback under
LIGHTNING_DEBUG=1. Removing the handler previously left thesuite green while every command printed raw tracebacks; it now fails three tests.
access and fail the same way on
main.