Skip to content

fix: disable gzip compression to silence [Gzip] drain listener warning - #1

Open
tigeregit wants to merge 1 commit into
mainfrom
fix/gzip-drain-listener-warning
Open

tigeregit wants to merge 1 commit into
mainfrom
fix/gzip-drain-listener-warning

Conversation

@tigeregit

Copy link
Copy Markdown
Owner

问题

Windows(以及其他平台)上运行 agentboard serve,浏览器首次加载 dashboard 后终端反复出现:

(node:38544) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 drain listeners added to [Gzip]. MaxListeners is 10.

根因(Next.js 框架 bug,非本项目代码)

  1. Next 16.3 起,App Router 的 HTML/RSC 响应通过 pipeNodeReadableToNodeResponse 输出;每当 res.write() 返回 false(gzip 压缩缓冲写满、产生背压),它就执行一次 res.once('drain', ...)。
  2. Next.js 内置的 compression 中间件会把 res.on 转发到底层 zlib Gzip 流,但没有转发 removeListener(上游缺陷 expressjs/compression#153,2019 年挂起至今,compression 1.8.1 仍未修)。于是这些 once 监听器挂在 Gzip 流上永远不会被移除——每次背压写泄漏一个,第 11 个触发 Node 的 MaxListenersExceededWarning。
  3. 已核对源码:16.3.4(当前依赖)和最新稳定版 16.3.5 均带此 bug;官方修复 vercel/next.js#97698(每个响应只挂一个常驻 drain 监听器)2026-08-24 才合并,首个包含修复的版本是 16.4.0-canary.10,尚无稳定版。

也就是说:浏览器打开 dashboard 时,流式渲染的 HTML/RSC 响应经过 gzip 压缩产生背压,每写满一次缓冲区就泄漏一个监听器,刷几次页面 warning 就出现几次。

修复

在 next.config.ts 设置 compress: false:

  • dashboard 只在 loopback / 局域网提供服务,gzip 压缩在这些链路上没有实际收益,只增加 CPU 开销;
  • 关掉压缩后不再创建 Gzip 流,从根上消除监听器泄漏(而不是用 setMaxListeners 掩盖症状);
  • 不依赖 canary 版 Next.js。待 16.4.0 稳定版发布并升级后,如需重新开启压缩可还原此行。

验证

本地 npm run build && agentboard serve(或 npm run start)后浏览器访问 dashboard,MaxListenersExceededWarning 不再出现。

Next.js <=16.3.x (incl. 16.3.4 and latest stable 16.3.5) leaks one
`drain` listener per backpressured write onto the vendored compression
middleware's Gzip stream when piping App Router HTML/RSC responses,
producing "MaxListenersExceededWarning: 11 drain listeners added to
[Gzip]" (vercel/next.js#97698; upstream compression PR
expressjs/compression#153 has been open since 2019).

The fix only ships in 16.4.0-canary.10+. Since the dashboard is served
over loopback/LAN where gzip gains nothing, set `compress: false` —
this removes the Gzip stream entirely instead of masking the warning
with setMaxListeners.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant