Multi-GiB foreground stdout can fail with V8 string-length fatal or empty stdout
What happened?
While validating recent long-session memory work, I found a separate extreme large-output failure in the foreground shell stdout path.
The long-session stress portion no longer reproduced the original heap OOM symptoms under the default Node heap:
- no
JavaScript heap out of memory
- no
Reached heap limit
- no
Ineffective mark-compacts near heap limit
- no
Allocation failed
However, an amplified foreground stdout stress test still failed near a 2048 MiB payload. The tested CLI was a local build of Qwen Code, with default heap settings and no NODE_OPTIONS.
Observed results:
| Model |
2048 MiB foreground stdout result |
pai/glm-5 |
exit=1, empty stdout, no standard OOM text |
qwen3.6-plus |
exit=1, empty stdout, no standard OOM text |
DeepSeek/deepseek-v4-pro |
V8 fatal: Check failed: i::kMaxInt >= len |
The DeepSeek run's native stack included:
Fatal error in , line 0
Check failed: i::kMaxInt >= len.
...
v8::String::NewFromOneByte
node::StringBytes::Encode
node::encoding_binding::BindingData::DecodeUTF8
This looks different from the original long-session heap OOM. The failure path appears to be multi-GiB foreground shell stdout being decoded or constructed as a JavaScript string.
Reproduction
The test harness asked Qwen Code to run foreground shell commands that stream large stdout payloads. The payload command shape was:
node -e "const chunk='x'.repeat(1024*1024); for (let i=0; i<N; i++) process.stdout.write(chunk)"
Payload sizes covered:
128 MiB
256 MiB
512 MiB
1024 MiB
1536 MiB
2048 MiB
All three tested models completed through 1536 MiB. The 2048 MiB payload triggered the failures above.
Concrete environment:
CLI: local Qwen Code build
Displayed CLI version: 0.15.11
Node.js: v22.22.0
OS: macOS arm64
Node default heap limit: 4144 MiB
NODE_OPTIONS: unset
Explicit --max-old-space-size: unset
Runner ulimit: unset
Auth type: openai-compatible API config
A minimized test should route a command that streams about 2 GiB of stdout through the same foreground shell/tool-result path. For example:
node -e "const chunk=Buffer.alloc(1024*1024, 120); for (let i=0; i<2048; i++) process.stdout.write(chunk)"
The important part is that the output is streamed through Qwen Code's foreground shell output handling, not merely printed directly by a standalone child process outside Qwen Code.
What did you expect to happen?
Qwen Code should not crash or exit with empty stdout when a foreground shell command produces very large stdout.
For outputs beyond a safe limit, Qwen Code should fail in a controlled way, for example by:
- truncating or summarizing the captured stdout,
- spilling large output to a file and returning a bounded preview,
- enforcing an explicit max captured-output budget, or
- returning a clear actionable error before constructing a multi-GiB JavaScript string.
Client information
Client Information
CLI: local Qwen Code build
CLI version shown by package: 0.15.11
Node.js: v22.22.0
OS: macOS arm64
Node default heap limit: 4144 MiB
NODE_OPTIONS:
Explicit --max-old-space-size: unset
Runner ulimit: unset
Login information
OpenAI-compatible API configuration. The issue does not appear auth-specific.
Models used in the stress run:
pai/glm-5
qwen3.6-plus
DeepSeek/deepseek-v4-pro
Anything else we need to know?
This should be tracked separately from the long-session/resume heap OOM issue:
- The original issue path is long-session history, compaction, and clone pressure.
- This issue path is foreground shell stdout decode/string construction.
- In the same validation run, realistic long-session and multi-agent stress did not reproduce the traditional heap OOM symptoms.
- The large stdout path only failed at the intentionally extreme
2048 MiB payload; 128 MiB through 1536 MiB completed.
This is also distinct from the oversized resumed-history Invalid string length follow-up. Both involve V8 string-size boundaries, but they occur in different data paths:
- resumed history issue: model-facing history/request serialization after
--resume
- this issue: foreground shell stdout capture/decode/result handling
I did not prove this is introduced by #4286. I did not run the same 2048 MiB stdout payload against the pre-PR baseline, and the failure was found while validating a separate long-session memory fix. This looks like a follow-up robustness issue rather than evidence that the long-session heap OOM fix failed.
Related:
Suggested labels:
type/bug
category/core
category/performance
scope/memory-usage
Multi-GiB foreground stdout can fail with V8 string-length fatal or empty stdout
What happened?
While validating recent long-session memory work, I found a separate extreme large-output failure in the foreground shell stdout path.
The long-session stress portion no longer reproduced the original heap OOM symptoms under the default Node heap:
JavaScript heap out of memoryReached heap limitIneffective mark-compacts near heap limitAllocation failedHowever, an amplified foreground stdout stress test still failed near a 2048 MiB payload. The tested CLI was a local build of Qwen Code, with default heap settings and no
NODE_OPTIONS.Observed results:
pai/glm-5exit=1, empty stdout, no standard OOM textqwen3.6-plusexit=1, empty stdout, no standard OOM textDeepSeek/deepseek-v4-proCheck failed: i::kMaxInt >= lenThe DeepSeek run's native stack included:
This looks different from the original long-session heap OOM. The failure path appears to be multi-GiB foreground shell stdout being decoded or constructed as a JavaScript string.
Reproduction
The test harness asked Qwen Code to run foreground shell commands that stream large stdout payloads. The payload command shape was:
node -e "const chunk='x'.repeat(1024*1024); for (let i=0; i<N; i++) process.stdout.write(chunk)"Payload sizes covered:
128 MiB256 MiB512 MiB1024 MiB1536 MiB2048 MiBAll three tested models completed through
1536 MiB. The2048 MiBpayload triggered the failures above.Concrete environment:
A minimized test should route a command that streams about 2 GiB of stdout through the same foreground shell/tool-result path. For example:
node -e "const chunk=Buffer.alloc(1024*1024, 120); for (let i=0; i<2048; i++) process.stdout.write(chunk)"The important part is that the output is streamed through Qwen Code's foreground shell output handling, not merely printed directly by a standalone child process outside Qwen Code.
What did you expect to happen?
Qwen Code should not crash or exit with empty stdout when a foreground shell command produces very large stdout.
For outputs beyond a safe limit, Qwen Code should fail in a controlled way, for example by:
Client information
Client Information
Login information
OpenAI-compatible API configuration. The issue does not appear auth-specific.
Models used in the stress run:
pai/glm-5qwen3.6-plusDeepSeek/deepseek-v4-proAnything else we need to know?
This should be tracked separately from the long-session/resume heap OOM issue:
2048 MiBpayload;128 MiBthrough1536 MiBcompleted.This is also distinct from the oversized resumed-history
Invalid string lengthfollow-up. Both involve V8 string-size boundaries, but they occur in different data paths:--resumeI did not prove this is introduced by #4286. I did not run the same 2048 MiB stdout payload against the pre-PR baseline, and the failure was found while validating a separate long-session memory fix. This looks like a follow-up robustness issue rather than evidence that the long-session heap OOM fix failed.
Related:
Suggested labels:
type/bugcategory/corecategory/performancescope/memory-usage