fix: silence xterm.js parser diagnostics leaking from headless shell terminals#7663
Conversation
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with evidence — #7631 reports repeated Direction: aligned. The headless terminals in Size: 2 production logic lines (two Approach: the scope feels right. Two Terminal construction sites, one option added to each, one regression test. No unrelated changes, no drive-by refactors. I checked — these are the only two production Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的 bug,有证据——#7631 报告了 shell 输出包含不完整 ANSI 转义序列(code 176 / 0xB0,与 GBK/GB2312 输出一致)时 stderr 上反复出现 方向:对齐。 规模:2 行生产逻辑(两处 方案:范围合理。两个 Terminal 构造点各加一个选项,一个回归测试。无无关改动,无顺手重构。已确认这是代码库中仅有的两个生产 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code ReviewIndependent proposal: given "xterm.js parser diagnostics leak to stderr from headless terminals," I would find every production Comparison with the diff: the PR does exactly this. Two Terminal construction sites in No correctness bugs, security issues, or regressions found. Specifics:
CI Test Evidence
CI still running at review time: Not verified: live TUI behavior — this change suppresses an internal stderr diagnostic, not user-visible TUI output, so tmux real-scenario testing is not applicable. A maintainer can trigger the isolated 中文说明代码审查独立方案: 针对"xterm.js 解析器诊断信息从无头终端泄漏到 stderr"的问题,我会找到所有使用 与 diff 的对比: PR 正是这样做的。 未发现正确性 bug、安全问题或回归。 CI 测试证据审查时 CI 仍在运行: 未验证:实时 TUI 行为——此更改抑制的是内部 stderr 诊断,而非用户可见的 TUI 输出,因此 tmux 真实场景测试不适用。 — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 5/5 — clean across every stage; would merge without hesitation. This is exactly the kind of PR I like to see: a real user-reported bug (#7631, with concrete error output showing the 0xB0 parsing error), a two-line fix that addresses it at the right level, and a solid regression test that proves both the noise is gone and the parser still recovers. My independent proposal was identical to what the PR does — add The only open item is that If I had to maintain this in six months, I'd thank the author: the comments explain why the log level is off, the test documents the exact failure mode, and the diff is small enough to revert in seconds if ever needed. 中文说明置信度:5/5 —— 每个阶段都很干净;毫不犹豫地合并。 这正是我希望看到的 PR:一个真实的用户报告 bug(#7631,有具体的错误输出显示 0xB0 解析错误),一个在正确层面解决问题的两行修复,以及一个可靠的回归测试,证明噪声消失且解析器仍能恢复。 我的独立方案与 PR 完全一致——在两个无头 Terminal 构造处添加 唯一的开放项是审查时 — Qwen Code · qwen3.8-max-preview Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
— qwen3.7-max via Qwen Code /review
Maintainer verification — built and tested locally against real runtimesI verified this PR locally with no mocks: real 1. The reported failure reproduces end-to-end, and the PR eliminates it
Both byte patterns from the issue reproduce: 2. Controlled experiments: both edits and the test are load-bearingPer-site (E2E): patching only the replay terminal (L392) leaves 1 diagnostic; patching only the live PTY terminal (L1486) leaves 1; both → 0. Neither line is redundant — both construction sites fire on a single ordinary command. Per-site (unit test): running the PR's Amplification: 606 bytes of shell output containing 200 malformed sequences produced 307,484 bytes of stderr on base (400 diagnostics, ~507× amplification) versus 0 on the PR, with 3. Scope: nothing else is hidden, nothing else needs patchingScanning the shipped These are also the only two 4. Static checks and suites
The 6 local failures are not attributable to this PR — the identical 6 fail on the base worktree ( Notes (non-blocking)
中文说明维护者验证 —— 本地构建并针对真实运行时测试我在本地以完全无 mock 的方式验证了本 PR:真实的 1. 端到端复现了报告的故障,本 PR 将其消除
issue 中的两种字节模式都能复现: 2. 对照实验:两处改动与新增测试都是「承重」的按站点(E2E): 只修 replay 终端(L392)仍剩 1 条诊断;只修实时 PTY 终端(L1486)仍剩 1 条;两处都修 → 0 条。两行都不冗余——一条普通命令就会同时触发两个构造点。 按站点(单元测试): 把 PR 的 噪声放大: 606 字节、含 200 个畸形序列的 shell 输出,在 base 上产生 307,484 字节 stderr(400 条诊断,约 507 倍放大),在 PR 上为 0,且两侧 3. 范围:没有隐藏其他信息,也没有遗漏其他站点扫描随包发布的 这两处也是 4. 静态检查与测试套件
这 6 个本地失败与本 PR 无关——在 base worktree 上同样是这 6 个失败( 说明(不阻塞合并)
|


What this PR does
Sets
logLevel: 'off'on the two headless@xterm/headlessTerminalinstances inpackages/core/src/services/shellExecutionService.ts— the replay terminal used to reserialize captured output and the live PTY terminal used during shell execution — and adds a regression test. A brief inline comment at each construction site records why the log level is disabled so it is not dropped in a later edit.Why it's needed
When a shell command emits an incomplete or invalid ANSI escape sequence — for example an
ESCbyte followed by0xB0, which is common with GBK/GB2312 (non‑UTF‑8) output — xterm.js's defaultLogServicewrites anxterm.js: Parsing error: {...}line toconsole.error. The parser recovers gracefully with no data loss and continues processing subsequent bytes, so the message is purely diagnostic. Because these two terminals run inside the shell execution service only to capture and serialize output, that benign diagnostic escapes to stderr and surfaces as alarming, repeated noise for anything that forwards child‑process stderr verbatim (the case reported in #7631). In a normal interactive TUI the same message is swallowed and never seen. These terminals never render diagnostics to a user, so turning their internal logging off is the appropriate scope for silencing the noise without changing any captured output.Reviewer Test Plan
This is a small, non‑user‑visible change (it suppresses an internal stderr diagnostic), so the plan below is a focused unit test plus a direct before/after probe of the library behavior rather than a TUI screenshot.
How to verify
Run the focused unit tests:
All tests pass, including the new case
suppresses parser diagnostics for malformed PTY output, which feeds anESC+0xB0sequence followed by normal text through the shell execution path with aconsole.errorspy, then asserts that noParsing errorline is emitted while the captured output still contains the trailing text (proving the parser still recovers).The underlying behavior can also be confirmed directly against
@xterm/headless: constructing aTerminaland writing"\xb0recovered"emits oneconsole.errorcontainingParsing errorby default and zero oncelogLevel: 'off'is set; in both cases the buffer still readsrecovered.Evidence (Before & After)
Not user‑visible (this suppresses an internal stderr diagnostic, not TUI output). Verified via the new unit test and a direct
@xterm/headlessprobe:{ allowProposedApi: true }): writing\xb0recovered→ 1console.errorcall containingParsing error; buffer line =recovered.{ allowProposedApi: true, logLevel: 'off' }): same write → 0console.errorcalls; buffer line =recovered.Tested on
Environment (optional)
Unit tests only (
vitest); no runtime sandbox needed.Risk & Scope
Linked Issues
Closes #7631
中文说明
在
packages/core/src/services/shellExecutionService.ts中的两个@xterm/headless无头Terminal实例上设置logLevel: 'off'——一个是用于重新序列化已捕获输出的 replay 终端,另一个是 shell 执行期间使用的实时 PTY 终端——并新增一个回归测试。每个构造处都加了一行简短注释,说明为何关闭日志级别,以免日后被误删。当 shell 命令输出不完整或无效的 ANSI 转义序列时(例如
ESC字节后跟0xB0,这在 GBK/GB2312 等非 UTF‑8 输出中很常见),xterm.js 默认的LogService会向console.error写入一行xterm.js: Parsing error: {...}。解析器会自动恢复且不丢失数据,因此该信息纯属诊断性质。由于这两个终端仅用于在 shell 执行服务内部捕获和序列化输出,该诊断信息会外泄到 stderr,对任何原样转发子进程 stderr 的场景(即 #7631 报告的情况)表现为反复出现、令人担忧的噪声;而在正常的交互式 TUI 中同样的信息会被吞掉、从不显示。这些终端从不向用户呈现诊断信息,因此关闭其内部日志是在不改变任何已捕获输出的前提下消除该噪声的恰当范围。验证:
cd packages/core && npx vitest run src/services/shellExecutionService.test.ts,全部测试通过,包括新增用例suppresses parser diagnostics for malformed PTY output。修复前:写入\xb0recovered会产生一次包含Parsing error的console.error;修复后:不再产生该调用,且缓冲区仍为recovered。仅在 macOS 上本地验证(单元测试)。AI was used for assistance.