Repository navigation
Conversation
…ream The native coder kept only a message string for a failed codec call, so the TypeError of a DecompressionStream had no code. It now keeps what the codec said (the zlib return code and msg, the brotli decoder error code, the zstd error code, or that the input ended early) and builds one TypeError from it, as Node.js does for these streams: no message, own code, and as cause the Error that node:zlib reports for the same failure.
|
Updated 6:05 AM PT - Oct 29th, 2026
❌ @robobun, your commit 99e46ba has 1 failures in
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.5c097f64-4a83-4d26-8a32-6e37cea28e11
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.3883b95d-0c1d-4e70-bc3a-79517910f363
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.cdcc2103-33bd-4f40-a7ef-77b31ee094b3
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.6a2dd875-db42-4183-bed9-d153c6396796
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.242c65d5-9320-490f-b687-6ee652c7bbd7
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.4b7b9d53-10a5-43bf-93cf-286945307e84
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.9aa332aa-bd04-475f-99e1-f201594e613c
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.76d57f40-1c56-4a05-b857-c4e7628443aa
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.aee23059-7350-48ce-9b31-7875db90a93d
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.3587e21d-e664-4339-808e-92dd71c15718
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.8ebefc8f-fb60-4280-ad7e-280da75a2871
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.43ca1daa-8b6a-4382-968d-646102d816c0
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.c09c9482-89e6-42e9-a50b-35458a7bf760
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.36b71f19-2a55-444a-94fe-e3abb6e8aba8
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.a8f80132-9a38-4249-937d-ccc60e28f4e9
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.0e8e44db-1ba3-4697-974a-68e29c461f71
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.dafbda3d-d327-4d9b-bf78-74cc2a44e526
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.8958f0a7-b451-43c2-85d9-b0bcd19dee3d
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.4c2bcb0a-f4cc-49c4-b825-859e706aa245
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.d19d41fb-04a2-4698-8928-bedd824611b6
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.aac9f4c7-c73e-41d2-9b8a-abd5b1a8bcc7
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.db1ecb6a-2731-47a6-8585-db4d4e931ffb
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.8a397804-4f49-4910-99f8-e8f9c4837610
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.16e58439-9ab7-4fa0-96ca-3c9fbd9ee077
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.49fec9d1-989b-4ab2-99d0-03d5b4af2401
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.e7dcdda2-1ce0-4ae9-a55b-b96737307913
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.0ccdacf4-e9a3-4f17-9f8b-cb817a725b60
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.741c74f8-458e-41ca-9731-05465799641e
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.0adb5e08-9ca4-4833-ad18-05a7554f7d7a
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.a809709c-4efd-4d1a-8d88-bfeed247dce7
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.6357dd13-5c32-455d-9def-40800f914f3a
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.333fd6c0-b7af-4e27-81c7-b25324bddb72
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.8a9d67f1-3d64-4b18-9a92-755057f2d01a
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.15d34c90-07b7-4ce4-ae6a-6beec2815bfb
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.2ea58fe6-5288-488d-928a-3d264a7db8a5
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.93f1780f-2f2f-4ce9-9341-802b80125c19
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.34ebb8c2-b32e-4439-b3b5-827fce292ee4
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.8e1e08c3-6ac2-4743-ae1a-0fa7ec6cf33e
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.87abfe7a-f231-4aff-adc1-1d06b339d7be
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.8e76d380-1dd4-4d94-954f-f0baa7c339c0
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.4d4f642c-fbf1-4fe5-aabf-ab65b0f5ca12
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.a7ddac2e-c4c5-4bd4-9abf-5269593ea6d5
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.b23cc3f1-9b5c-464c-b29d-8fc8469cfe0a
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.0d629fef-bb1d-4c44-b160-0159af0aeed8
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.de54c369-b37f-4a92-b6ed-0853b5787114
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.4d3fcaa7-201e-4643-8677-d9a55fefc806
CapacityLimitExceededThis account has reached its capacity limit. Reach out to your account team for assistance.d12d75bd-6073-417e-8485-5e71da8b8ced🧪 To try this PR locally: bunx bun-pr 47272That installs a local version of the PR into your bun-47272 --bun |
|
Status How I reproduced it: a script writes bad input to
The user case: CI: no test of this change failed in any run. The red check is a CI machine problem.
To get a green check, a maintainer can retry the canceled jobs of build 135156. PR: #47272 |
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 31 seconds. View limit details
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Codec error names and messages src/zlib_sys/shared.rs, src/zstd/lib.rs, src/zlib/stream.rs, src/runtime/node/zlib/NativeZlib.rs, src/runtime/node/zlib/NativeZstd.rs |
Zlib and Zstd helpers provide error names and messages. Native callers use the helpers, and ZStream::error_message now returns a static lifetime. |
Typed codec failures and stream handling src/runtime/webcore/CompressionStreamCoder.rs |
CompressionStreamCoder retains typed codec errors, reports truncation and trailing data, and returns no coder when codec initialization or configuration fails. Gzip can continue to another member; other inflate formats report trailing data. |
JavaScript error conversion and stream tests src/runtime/webcore/CompressionStreamCoder.rs, test/js/web/streams/compression.test.ts |
Codec failures become TypeError objects with a code and a cause containing codec details. Tests cover malformed and truncated input, stream error consistency, file sinks, and gzip members. |
Possibly related PRs
- oven-sh/bun#47073: Replaces the Brotli C backend with Rust APIs and typed decoder errors used by this change.
- oven-sh/bun#46607: Introduces
ZStream, which this change updates to expose static error-message data. - oven-sh/bun#47079: Replaces the zstd C backend with typed errors used by this change.
Suggested reviewers: jarred-sumner, dylan-conway
Priority:
Merge Risk: 
Minimal · previous review 32727
-
No actionable issue is established for this change; it is mergeable after normal checks.
Pre-merge checks: 
4
-
Full details: Title check
-
Explanation
The title names the main change: failed DecompressionStream streams now report the codec's code and cause on the TypeError. The changeset matches this. It adds typed codec errors (zlib, brotli, zstd, truncation), builds a TypeError with an own code and a cause, and adds tests for these shapes. The title is specific, concise, and single-sentence.
Full details: Description check
-
Explanation
The PR description follows the repository template. It has both required sections: "Problem"/"Fix"/"Background"/"Downsides" cover the purpose, and the "Verified" line and the Notes block give the verification details, including the new test file, the 29-of-30 failing-on-main result, the ASan debug suite runs, and the 32-of-32 probe comparison against Node.js. The template headings "What does this PR do?" and "How did you verify your code works?" are not used verbatim, but their content is present. Gaps: Windows and macOS were not tested, and one test timed out when run with other files, so the verification is partial. The description also states that error.message changes to empty, which is a user-visible behavior change. It is covered under Downsides.
-
Finishing Touches: 💡 1
-
🛠️ Fix failing CI checks: 💡
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Beyond the inline finding, I also checked the 'static claim on ZStream::error_message (zlib only points msg at string literals or z_errmsg entries, so carrying it from the thread pool is sound), that the new bun_zstd::error_code_name table has an arm for every ZSTD_error_* constant bound in src/zstd/lib.rs, and that every EncodedSlice::latin1 input in Failure::to_js is an ASCII code name.
Extended reasoning...
The change replaces the string-only codec error in src/runtime/webcore/CompressionStreamCoder.rs with a Copy+Send Failure enum and builds a Node-shaped TypeError (own code, non-enumerable cause with message/errno/code), with small helper additions in src/zlib_sys/shared.rs, src/zlib/stream.rs and src/zstd/lib.rs and 30 new tests. It touches no auth, crypto or injection surface; the risk is behavioral compatibility of error codes on malformed input, which is the subject of the inline finding.
|
Merged main (da867c6) into this branch so that CI runs on a green main. There was no conflict.
|
Problem
fileTypeFromBuffer()(file-type21.3.1 to 22.1.1) rejects withTypeError: inflate failedfor a ZIP with a corrupt entry. Node.js and 1.3.14 return{ ext: "zip" }. The package needserror.code, which a failedDecompressionStreamlost in 1.4.0.CompressionStreamCoder::run(src/runtime/webcore/CompressionStreamCoder.rs:490on main) madeCodecError::Message(&'static str)of every codec failure (webstreams: native CompressionStream/DecompressionStream/TextEncoderStream/TextDecoderStream #36695).Fix
CodecError::Codec(Failure)keeps the zlib return code andmsg, the brotli or zstd code, orTruncated.Messageis gone:runcannot return a bare label.Failure::to_jsbuilds Node's error for all five formats: aTypeError, no message, owncode.causeis thenode:zlibErrorfor the same bytes.test/js/web/streams/compression.test.ts(30 new tests, 29 fail on main). Other suites: Notes.Background
CompressionStreamCoderis one stream's native state. Large chunks decode on the thread pool, so its error isCopyandSend.causeis Node's shape (on main from Upgrade reported Node.js version to 26.3.0 #31991 until webstreams: native CompressionStream/DecompressionStream/TextEncoderStream/TextDecoderStream #36695). The empty message is Node's and 1.3.14's:@zip.js/zip.js2.8.29 to 2.16.1 maps only message-less errors.node:zlib's engine under the streams: Make CompressionStream native #31728's review rejected it.Downsides
error.messageis empty (1.4.x:inflate failed). The text iscause.message.buntext +224 bytes, trimmed executable withDecompressionStream+8,192 bytes. A failed stream makes 2 error objects (was 1). A valid step runs -1 to +6 instructions inrun.Z_BUF_ERRORorZ_DATA_ERROR. Node.js reports trailing junk (DecompressionStream: deliver the output decoded ahead of trailing junk before the error #43719).Notes
No issue reports this. It came from a differential run against Node.js and Bun 1.3.14. The consequence is a rejected promise or a wrong status on malformed input: no crash, no data loss, nothing on a valid stream.
file-type22.2.0 no longer needs the code. 21.3.4 is the last 21.x.The report's script (gzip, deflate, deflate-raw: input that ends early, a wrong checksum, another format; 7 rows). Rows that lack the code: 0 on Node.js v26.3.0 and v26.12.1, 0 on Bun 1.3.14, 7 on Bun 1.4.0, 1.4.4 and main 11c3671, 0 on this branch.
Users of the shape, run here.
file-type21.3.4 and 22.1.1 (core.js:recoverableZipErrorCodeshasZ_BUF_ERROR,Z_DATA_ERROR,ERR_INVALID_STATE). Five ZIPs, four with a corrupt or cut-off entry: Node.js and 1.3.14 resolve 5 of 5, 1.4.4 resolves 1 of 5, this branch 5 of 5.@zip.js/zip.js2.8.29, 2.15.0 and 2.16.1 (mapInflateStreamError:if (error && error.message) throw error, elseInvalid compressed data). Node.js and 1.3.14: mapped. 1.4.4: not mapped. This branch: mapped. 2.17.0 maps on all of them.The error, per failure.
TypeError, no ownmessage, own propertiescause(not enumerable) andcode(enumerable, writable, configurable).causeis a plainErrorwithmessage,errno,code.codecause.messagecause.errnoZ_BUF_ERRORunexpected end of fileZ_DATA_ERRORincorrect header checkZ_NEED_DICTMissing dictionaryERR__ERROR_FORMAT_PADDING_1and so onDecompression failedZSTD_error_prefix_unknownand so oncodeand its attributes,causeand its attributes,cause's class, message, keys,errnoandcodeare equal to Node.js v26.3.0 and v26.12.1 on 32 of 32.causeequals whatzlib.gunzipSync,inflateSync,inflateRawSync,brotliDecompressSyncandzstdDecompressSyncthrow in Bun for the same bytes. The tests assert this for 20 rows.zlib.zstdDecompressSyncreturns without an error (node:zlib: zstd decoder follows Node v26.10.0 across writes and at the end of the input #33413 changes that). Node.js v26.6.0 and v26.12.1 throwZ_BUF_ERROR,unexpected end of file, errno -5 from that call, which is thecausebuilt here (ZstdDecompressContext::GetErrorInfoinsrc/node_zlib.cc). v26.5.1 returns without an error. Node.js has nozstdstream.read(), the failingwrite()andclose()reject with the same object. This holds on the JS thread, on the thread pool (a chunk over 128 KiB), in a later step of one chunk, and with a native sink (Bun.write).RangeError.ERR_TRAILING_JUNK_AFTER_STREAM_ENDis unchanged.Changes that users of 1.4.x see.
TypeErrorhas no message. 1.4.x hadinflate failed,unexpected end of file,Missing dictionary,brotli decode failed,zstd decode failed. A message on theTypeError(equal tocause.message) is two lines inFailure::to_js. Then@zip.js/zip.jsup to 2.16.1 stays not mapped.cause.messagewas the decoder's name (_ERROR_FORMAT_PADDING_1) and is nowDecompression failed, as innode:zlib.causegetserrnoand is no longer enumerable.ZSTD_error_*code. 1.3.14 had that code on a plainError. Here it stays aTypeError, as the spec asks.codeis set withput, not throughErrorCode.ts.ErrorCode.tsputscodeon a prototype per code (createErrorPrototypeinErrorCode.cpp) and has no entry for the zlib, brotli or zstd names. Node.js, Bun 1.3.14 and the existing brotli test have an owncode. #41737 makescodeown forERR_*errors.Measured on release builds (lto, linux x64) of main 11c3671 and this branch.
size_ofCodecError/Option<CodecError>/Result<bool, _>/Result<Progress, _>: 24 / 24 / 24 / 24 bytes (main: 24 / 24 / 24 / 24),Sendasserted. The asserts compile for linux x64,x86_64-pc-windows-msvcandaarch64-apple-darwin.size bun: text 138,011,918 to 138,012,142 (+224), data and bss equal, file size equal (138,197,144).run(): 4175 bytes (main 4198).codec_error_to_js: 824 (main 462). New:zlib_error83,zstd_error39,bun_zstd::error_code_name40.run()for a typical valid step (gdbnexti, 200 steps, fixed workloads), main to branch: inflate 114 to 117, deflate 99 to 101, brotli decode 86 to 85, brotli encode 80 to 86, zstd decode 89 to 90, zstd encode 75 to 78. The difference is register moves, two alignment nops and a jump to a shared epilogue. No call and no allocation is added. In the inflate arm, the jump table over zlib's return code (one indirect jump) is one conditional branch now.heapStats, 2000 streams): deflate 1 to 2, brotli 2 to 2, zstd 1 to 2.node:zlibcheck_error, no-error path (gdbstepi): zlib 13 to 18 instructions (main 13 to 18), zstd 23 (main 48).NativeBrotli.rsis not edited.CompressionStreamCoder__exports: 5 (main 5). Host functions, codegen rows, C++ files changed: 0.--trimmable=on):bun build --compile --trim84,315,336 to 84,319,432 bytes (+4,096).bun compile --trim: hello world 25,620,680 to 25,620,616 (-64), aDecompressionStreamprogram 38,574,216 to 38,582,408 (+8,192). The output of each trimmed executable equals the untrimmed run. Entry points (7360) and edges between features (332) are equal.Not changed here, and where it is tracked.
Z_BUF_ERROR(one byte) orZ_DATA_ERROR(more) where Node.js reportsERR_TRAILING_JUNK_AFTER_STREAM_END. Node.js v26.5.1 and v26.12.1 report trailing junk for every byte after the first member, a second member included (v26.3.0 goes on with the next member). DecompressionStream: deliver the output decoded ahead of trailing junk before the error #43719 rewrites the same two places (next_memberhere) and delivers the output before that error.file-type22.2.0 a valid docx or epub that has an unsigned data descriptor is reported aszipon 1.4.4 and on this branch for that reason (Node.js and 1.3.14:docx,epub).zlib.zstdDecompressSyncand the zstd streams ofnode:zlibdo not report a cut-off frame: node:zlib: zstd decoder follows Node v26.10.0 across writes and at the end of the input #33413.ZSTD_error_noForwardProgress_inputEmpty: Replace the zstd C library with a port to safe Rust #47079.causeis lost instructuredClone: webcore: preserve Error cause across structuredClone/postMessage/v8 serialization #31525.bun_zstd::error_code_nameiszstd::Error::name()there.write()of the bad chunk rejects. This PR does not change that. Since 1.4.0,writer.write(badChunk)rejects with the sameTypeErrorthat the reader gets. Node.js and Bun 1.3.14 fulfill that write and report the error on the readable side and onclose(). The Streams Standard says reject: the transform of these streams throws aTypeErroron bad input (Compression Standard, "decompress and enqueue a chunk"), andTransformStreamDefaultControllerPerformTransformends with "Throw r", which is the result of the write. A program that does not await or catch that promise gets an unhandled rejection, so it exits with code 1 where Node.js and 1.3.14 exit with 0.gzip,deflate,deflate-rawandbrotli: Node.js v26.3.0 and Bun 1.3.14 fulfill the write (0 unhandled rejections, exit 0). Bun 1.4.4 and the canary build 367d939 reject it (1 unhandled rejection, exit 1).unhandledrejectionevent there.a single write completes without a reader attachedpins that), so it rejects the bad chunk at once. Node.js fulfills that write.compression/decompression-corrupt-input.any.jsputs.catch()on its ownwrite()andclose().write()rejects with the same object as the read. Whether the standard's way stays is a maintainer's call.Self-reviewed: 12 concerns raised, 11 addressed. Removed after the review: a rule that made a zero byte after a gzip member trailing junk (it lost 260 bytes of output for a member plus one zero byte in the same chunk). Changed after the review: the
TypeErrorhas no message. Rejected: keep that gzip rule here. It is #43719's decision. Not taken from one addressed concern: the fix for the zstd stream with 16 empty chunks, because #47079 has it.Suites run on the ASan debug build.
test/js/web/streams/compression.test.ts(103 pass), the new tests withBUN_JSC_validateExceptionChecks=1,test/js/node/zlib/zlib.test.jsand four more files intest/js/node/zlib,test/js/bun/util/zstd.test.ts,test/regression/issue/{18413*,25432,29225}.test.ts,test/js/web/fetch/fetch-{gzip,compress}.test.ts, 63 vendored Node.js tests (test-zlib-*.js,test-whatwg-webstreams-compression.js,test-stream-iter-transform-errors.js), andtest-webstreams-compression-bad-chunks.js,test-webstreams-decompression-reject-trailing.js,test-webstreams-compression-buffer-source.js,test-webstreams-adapters-sync-write-error.js,test-zlib-type-error.js. One test timed out when 15 files ran together and passes alone:libdeflate gzip doesn't write past its output bufferinzlib.test.js(5 s limit, 4.1 s alone). Not run by hand: Windows and macOS.