From eaa9044520f51c5f4ea4c0f2a1b7989d3a7fdb95 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Tue, 13 Oct 2026 18:25:07 +0200 Subject: [PATCH] fix(uploads): drain a refused video body before answering (#606 merge-time fix) The ?kind=video branch called req.file(), which pipes the request into busboy, then answered 400 (unsupported container) or 403 (upload dir not a regular directory) without reading part.file. The server then stopped reading the body, and a browser fetch, which reads the response only after its upload is sent, hung on "Uploading 1/1 file" for an Android 3gp or an .mkv. Both early answers now consume the part first, as the mislabeled container path already did. (Reviewer-verified in Chromium: hang before, 400 in about 0.2 s after; inject and a Node client do not reproduce the hang.) Co-Authored-By: Claude Opus 5.5 (1M context) --- src/web/routes/session-routes.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/web/routes/session-routes.ts b/src/web/routes/session-routes.ts index ae8ab4a1..77f3b5fd 100644 --- a/src/web/routes/session-routes.ts +++ b/src/web/routes/session-routes.ts @@ -11,6 +11,7 @@ import { homedir } from 'node:os'; import { existsSync, statSync, mkdirSync, writeFileSync } from 'node:fs'; import { execFile } from 'node:child_process'; import fs from 'node:fs/promises'; +import { finished } from 'node:stream/promises'; import { randomBytes, randomUUID } from 'node:crypto'; import { performance } from 'node:perf_hooks'; import { @@ -5482,6 +5483,7 @@ export function registerSessionRoutes( const videoMime = (part.mimetype || '').toLowerCase().match(/^video\/(mp4|quicktime|x-m4v|webm|ogg)$/); if (videoMime) ext = VIDEO_MIME_EXT[videoMime[1]] ?? ext; if (!ext) { + await finished(part.file.resume()).catch(() => {}); reply.code(400); return createErrorResponse( ApiErrorCode.INVALID_INPUT, @@ -5490,6 +5492,7 @@ export function registerSessionRoutes( } const imageDir = await ensureUploadDir(session.workingDir); if (!imageDir) { + await finished(part.file.resume()).catch(() => {}); reply.code(403); return createErrorResponse(ApiErrorCode.INVALID_INPUT, `${UPLOADS_DIR} is not a regular directory`); }