Skip to content

Multi-GiB foreground stdout can fail with V8 string-length fatal or empty stdout #4364

@yiliang114

Description

@yiliang114

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions