Title: zlib: Brotli reset() drops quality and dictionary · Issue #66156 · nodejs/node · GitHub
Open Graph Title: zlib: Brotli reset() drops quality and dictionary · Issue #66156 · nodejs/node
X Title: zlib: Brotli reset() drops quality and dictionary · Issue #66156 · nodejs/node
Description: Version v27.0.0-pre (built from main at a2a051cd42; src/node_zlib.cc is unchanged on current main) Platform Linux x86_64 (Ubuntu 22.04, built with clang 20) Subsystem zlib What steps will reproduce the bug? Create a Brotli compressor wit...
Open Graph Description: Version v27.0.0-pre (built from main at a2a051cd42; src/node_zlib.cc is unchanged on current main) Platform Linux x86_64 (Ubuntu 22.04, built with clang 20) Subsystem zlib What steps will reproduce...
X Description: Version v27.0.0-pre (built from main at a2a051cd42; src/node_zlib.cc is unchanged on current main) Platform Linux x86_64 (Ubuntu 22.04, built with clang 20) Subsystem zlib What steps will reproduce...
Opengraph URL: https://github.com/nodejs/node/issues/66156
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"zlib: Brotli reset() drops quality and dictionary","articleBody":"### Version\n\nv27.0.0-pre (built from `main` at `a2a051cd42`; `src/node_zlib.cc` is unchanged on current `main`)\n\n### Platform\n\n```text\nLinux x86_64 (Ubuntu 22.04, built with clang 20)\n```\n\n### Subsystem\n\nzlib\n\n### What steps will reproduce the bug?\n\nCreate a Brotli compressor with a custom quality or a dictionary, call `.reset()` **before writing anything**, then compress as usual.\n\nThe quality/dictionary you passed in is gone. Nothing throws.\n\n```js\nimport * as zlib from 'node:zlib';\n\nconst input = Buffer.from('the quick brown fox jumps over the lazy dog '.repeat(100));\nconst dict = Buffer.from('the quick brown fox jumps over the lazy dog '.repeat(40));\n\nfunction compress(options, reset) {\n return new Promise((resolve, reject) => {\n const s = zlib.createBrotliCompress(options);\n const chunks = [];\n s.on('data', (d) => chunks.push(d));\n s.on('error', reject);\n if (reset) s.reset();\n s.end(input);\n s.on('end', () => resolve(Buffer.concat(chunks).length));\n });\n}\n\nconst q0 = { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 0 } };\nconsole.log('quality 0, no reset:', await compress(q0, false)); // 109\nconsole.log('quality 0, then reset:', await compress(q0, true)); // 55 (same as default quality 11)\nconsole.log('with dictionary, no reset:', await compress({ dictionary: dict }, false)); // 18\nconsole.log('with dictionary, then reset:', await compress({ dictionary: dict }, true)); // 55 (same as no dictionary)\n\nconst compressed = zlib.brotliCompressSync(input, { dictionary: dict });\nconst dec = zlib.createBrotliDecompress({ dictionary: dict });\ndec.reset();\ndec.end(compressed);\ndec.on('error', (err) => console.log('decompress after reset:', err.code));\n// ERR__ERROR_FORMAT_DICTIONARY\n```\n\n### How often does it reproduce? Is there a required condition?\n\nAlways. You do not need to write any data first. Just construct the stream with options and call `reset()`.\n\n### What is the expected behavior? Why is that the expected behavior?\n\n`reset()` should start a new Brotli session on the **same** stream, keeping the quality/params and dictionary you already set.\n\nThat is what the same API already documents for Zstd (`zlib.reset()`: \"start a new session while preserving the configured parameters and dictionary\"), and it is what `test/parallel/test-zlib-zstd-reset.js` asserts. Brotli goes through the same JS `reset()` method.\n\nIf the dictionary is dropped, a decoder cannot read a dictionary-compressed frame at all.\n\n### What do you see instead?\n\n- Quality 0 after `reset()` compresses as if you had used the default quality 11 (109 bytes become 55).\n- A dictionary after `reset()` compresses as if you had used no dictionary (18 bytes become 55).\n- `createBrotliDecompress({ dictionary })` then `reset()` fails with `ERR__ERROR_FORMAT_DICTIONARY` on a frame that was compressed with that dictionary.\n\nThe compressor never reports an error. The settings just disappear.\n\n### Additional information\n\nC++: `BrotliEncoderContext::ResetStream` / `BrotliDecoderContext::ResetStream` both do `return Init();` with no arguments. `Init()` immediately does `dictionary_.clear()` and creates a brand-new Brotli instance. It never replays `SetParams`.\n\nThis is not [#66087](https://github.com/nodejs/node/issues/66087) / [#66088](https://github.com/nodejs/node/pull/66088). Those are about `flush()` then `reset()` leaving a broken **frame**. Here there is no write at all — only the **configuration** is thrown away.\n\nLLDB on `src/node_zlib.cc` at `a2a051cd42` (same `ResetStream`/`Init` as current `main`):\n\n1. Stopped in `BrotliEncoderContext::ResetStream` at line 1514. Watch: `this->dictionary_` still has size **1760**. Next statement is `return Init();` with no dictionary argument.\n\n\u003cimg width=\"2620\" height=\"1596\" alt=\"ResetStream at line 1514: dictionary still size 1760, about to call empty Init()\" src=\"https://github.com/user-attachments/assets/2d03d57a-4e30-44e7-a931-33a173947bb0\" />\n\n2. That call lands in `Init` at line 1474, on `dictionary_.clear();`. Watch: the member `this->dictionary_` is **still 1760** (clear has not run yet), but the `dictionary` **parameter** is already size **0**, because `ResetStream` did not pass it in.\n\n\u003cimg width=\"3062\" height=\"1606\" alt=\"Init at line 1474: member dictionary still 1760, parameter dictionary already 0\" src=\"https://github.com/user-attachments/assets/c695f462-f74a-4902-98a9-a966a6521e0f\" />\n\n3. Continue to line 1483, `if (!dictionary.empty())`. Watch: both `this->dictionary_` and the `dictionary` parameter are now size **0**, so the attach-dictionary branch is skipped.\n\n\u003cimg width=\"3008\" height=\"1590\" alt=\"Init at line 1483: both dictionaries size 0, attach branch skipped\" src=\"https://github.com/user-attachments/assets/6c0d650e-05a9-44ed-a235-3366493b71c6\" />","author":{"url":"https://github.com/xia-chao","@type":"Person","name":"xia-chao"},"datePublished":"2026-09-20T12:42:16Z","url":"https://github.com/nodejs/node/issues/66156"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:484910a5-79e9-638a-4ad1-aa75cccda666 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 901C:3C34F7:72AD78:95A968:6AED31B8 |
| html-safe-nonce | 068d548a19ea5bb52ae6f3f1f54b4957a6b2f657a8e49e63a4d119f68e8d4d8a |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5MDFDOjNDMzRGNzo3MkFENzg6OTVBOTY4OjZBRUQzMUI4IiwidmlzaXRvcl9pZCI6IjQ5NzIxNzQ1MTM4NDQwNzI4ODgiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 8d9f08faebe11cc6b165c62a0c846842f7127a05e6a63a5f25db94210a3cdaa9 |
| hovercard-subject-tag | issue:5517869410 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/66156/issue_layout |
| twitter:image | https://opengraph.githubassets.com/fae284c8ea9d148490fc17e73acd3af8fec170c7696cccb5ec20f35ad43d7640/nodejs/node/issues/66156 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/fae284c8ea9d148490fc17e73acd3af8fec170c7696cccb5ec20f35ad43d7640/nodejs/node/issues/66156 |
| og:image:alt | Version v27.0.0-pre (built from main at a2a051cd42; src/node_zlib.cc is unchanged on current main) Platform Linux x86_64 (Ubuntu 22.04, built with clang 20) Subsystem zlib What steps will reproduce... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | xia-chao |
| hostname | github.com |
| expected-hostname | github.com |
| None | e51d0b7b5ab34b1688c05b54c3ed17b8658b2fa9bca973a9c740683ccb12cf4a |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 59ebb7b901cc154ecc179a44f266fcf69f343b30 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width