Accept MKV, and say why a file was refused - #11
Merged
Merged
Conversation
An .mkv upload had two ways to end badly. Small ones were refused for an extension nothing claimed, with "Unsupported file type" and no hint at what would have worked. Large ones never got that far: the whole body is sent before anything looks at it, so a clip over the request-body limit had its connection cut mid-send and reached the uploader as a bare "network error" — the same message a dropped connection gives, with nothing to suggest the size was the problem. Converting the clip to a smaller MP4 made both go away, which points at neither. Matroska needed nothing but the extension: it shares WebM's container, so the header check already recognised it and ffmpeg already takes frames from it. The refusals now happen in the browser, before a byte is sent. The handlers own the extension list and their own ceilings, so the page can hand the browser what the server takes and both stay in step; a file that doesn't fit is turned away as it's picked, named and measured against the limit it broke. The upload card says up front what does fit, the server repeats the same wording for anything that reaches it, and the XHR error case no longer pretends a severed upload was only ever the network. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYoVr4rkVdeQn5THttcGRM
Correcting the previous commit's account of the cause: the size limit is not what refused a 150 MB clip, and an unsupported extension does not produce a network error by itself. Uploading a 150 MB .mkv to the old code returns a clean JSON rejection, 200 and all. What does produce it is any failure the browser can't attribute to a response. Reproduced two: a body past the request-body limit dies after ~4 MB of a 250 MB send with a 500 and a severed connection, and a box that is gone or locked answers before the body is read at all, cutting off an upload already in flight. All three of those — plus a proxy hanging up on a body it thinks too big, and a genuinely dropped connection — reach the page as one bare error event carrying nothing to act on, and the page was turning every one of them into the same three words. So it stops guessing and asks. A failed upload is followed by a request small enough to answer when a large one just died, and what comes back separates box gone from box locked from box fine — the last meaning it was that upload in particular the server would not take. The same probe now covers a response that isn't ours, which an error page from a proxy would be. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYoVr4rkVdeQn5THttcGRM
The page was filling in its own explanation whenever the response didn't hand it one, and the explanation it reached for was the connection. That is how an unsupported format arrives as "network error": nothing on the wire was wrong, the client just had no reason to show and picked the wrong one. So the reason comes from the server now, on every path that can fail. A box that expired, a box that locked itself again, a malformed request and a body past the request limit all answer in the same shape as a success, with the reason in the body. The last of those never answered at all before — Kestrel throws on an over-limit body and the exception handler returns an HTML error page, so a 500 with no mention of size was all a 250 MB upload ever got back. Catching it turns that into a 413 that names the ceiling it broke. The page shows what it was told and nothing else. A status code with no body is reported as that status code, a reply that isn't ours is reported as one, and a request that got no reply says only that. No cause is asserted that wasn't given, which also removes the follow-up request added a commit ago: asking a second question is not how the first one should have been answered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYoVr4rkVdeQn5THttcGRM
Drops the format hint under the upload card and its styling, the progress-row styling, the generated accept attribute (the existing one just gains .mkv), and the commentary that had grown longer than the code it sat above. Tests keep the two that would fail if this regressed: an .mkv upload being stored and served as a video, and an over-limit body answering 413 with the reason rather than 500 and an HTML page. The rest asserted message wording and page markup, which the code they cover already reads plainly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RYoVr4rkVdeQn5THttcGRM
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.
An .mkv upload had two ways to end badly. Small ones were refused for an
extension nothing claimed, with "Unsupported file type" and no hint at what
would have worked. Large ones never got that far: the whole body is sent
before anything looks at it, so a clip over the request-body limit had its
connection cut mid-send and reached the uploader as a bare "network error" —
the same message a dropped connection gives, with nothing to suggest the size
was the problem. Converting the clip to a smaller MP4 made both go away, which
points at neither.
Matroska needed nothing but the extension: it shares WebM's container, so the
header check already recognised it and ffmpeg already takes frames from it.
The refusals now happen in the browser, before a byte is sent. The handlers
own the extension list and their own ceilings, so the page can hand the
browser what the server takes and both stay in step; a file that doesn't fit
is turned away as it's picked, named and measured against the limit it broke.
The upload card says up front what does fit, the server repeats the same
wording for anything that reaches it, and the XHR error case no longer
pretends a severed upload was only ever the network.