From d296e0ebcff26d1fe0409337ce7244366a13f3c6 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sun, 6 Sep 2026 00:11:00 +0700 Subject: [PATCH] fix(upload): wrap gzip chunk output in Buffer so Telegraf uploads work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of '400: Bad Request: there is no document in the request' on chunked uploads of compressible files (MP4, zip, text, etc.): Bun.gzipSync() returns a plain Uint8Array, NOT a Buffer. Telegraf (sendDocument/sendVideo) only recognises Buffer/Blob/stream sources as file uploads — a plain Uint8Array produces an empty multipart body and Telegram rejects the request. Verified via isolated forwardToStorage probes: - raw Buffer chunk (.bin, MP4) -> OK - plain Uint8Array chunk -> FAIL: there is no document - Buffer.from(gzip) chunk -> OK (after fix) - .gz extension on Uint8Array did NOT help (rules out MIME/extension) This only affected chunked uploads of compressible files; incompressible files (random .bin) worked by accident because gzip didn't shrink them so the raw Buffer was forwarded unchanged. --- src/shared/utils/compress.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/shared/utils/compress.ts b/src/shared/utils/compress.ts index bbebb1b..8b4f821 100644 --- a/src/shared/utils/compress.ts +++ b/src/shared/utils/compress.ts @@ -23,10 +23,16 @@ export const maybeCompressChunk = ( return { bytes: chunk, compressionAlgorithm: null }; } - const gzipped = Bun.gzipSync(chunk); + const gzipped = Bun.gzipSync(chunk as Uint8Array); if (gzipped.byteLength >= chunk.byteLength) { return { bytes: chunk, compressionAlgorithm: null }; } - return { bytes: gzipped, compressionAlgorithm: 'gzip' }; + // CRITICAL: Bun.gzipSync returns a plain Uint8Array, NOT a Buffer. + // Telegraf (telegram.sendDocument) only recognises Buffer/Blob/stream + // sources as file uploads — a plain Uint8Array yields an empty multipart + // body and Telegram rejects with "400: there is no document in the request". + // Wrap in Buffer.from(...) so chunked uploads of compressible files (MP4, + // zip, text, etc.) actually work. + return { bytes: Buffer.from(gzipped), compressionAlgorithm: 'gzip' }; };