Oversized resumed history can fail with Invalid string length
What happened?
While validating the long-session resume OOM work in #4286, I found a separate oversized-resume boundary.
Context:
This looks like the next boundary after the clone-OOM fix, not a new root cause introduced by #4286. Once the process survives resume/history cloning, the model-facing history can still be large enough that request serialization tries to build a JavaScript string beyond V8's string-length limit.
On my local Node build:
buffer.constants.MAX_STRING_LENGTH = 536870888
Reproduction
The reproduction below uses an isolated QWEN_RUNTIME_DIR and synthetic resumed history. It does not modify the real ~/.qwen sessions.
From the repository root:
ROOT=$(mktemp -d /tmp/qwen-resume-string-length.XXXXXX)
RUNTIME="$ROOT/runtime"
TEST_CWD="$PWD"
SESSION_ID=60000000-6000-4000-8000-600000000000
RUNTIME="$RUNTIME" TEST_CWD="$TEST_CWD" SESSION_ID="$SESSION_ID" node <<'NODE'
const fs = require('node:fs');
const path = require('node:path');
function sanitizeCwd(cwd) {
return cwd.replace(/[^a-zA-Z0-9]/g, '-');
}
const runtime = process.env.RUNTIME;
const cwd = process.env.TEST_CWD;
const sessionId = process.env.SESSION_ID;
const chatsDir = path.join(runtime, 'projects', sanitizeCwd(cwd), 'chats');
fs.mkdirSync(chatsDir, { recursive: true });
const file = path.join(chatsDir, `${sessionId}.jsonl`);
const out = fs.createWriteStream(file);
let parentUuid = null;
const payload = 'x'.repeat(1024 * 1024);
function write(record) {
out.write(JSON.stringify(record) + '\n');
parentUuid = record.uuid;
}
for (let i = 0; i < 600; i++) {
const type = i % 2 === 0 ? 'user' : 'assistant';
const role = type === 'user' ? 'user' : 'model';
write({
sessionId,
cwd,
uuid: `${sessionId.slice(0, 8)}-0000-4000-8000-${String(i).padStart(12, '0')}`,
parentUuid,
timestamp: new Date().toISOString(),
type,
message: { role, parts: [{ text: payload }] },
});
}
out.end();
out.on('finish', () => {
const sizeMB = (fs.statSync(file).size / 1024 / 1024).toFixed(1);
console.log(`${file} ${sizeMB} MB`);
});
NODE
Then run a Qwen Code build that contains the #4286 change against the generated session, with default heap settings:
env -u NODE_OPTIONS \
QWEN_RUNTIME_DIR="$RUNTIME" \
NO_COLOR=1 \
QWEN_DISABLE_TELEMETRY=1 \
npm run dev -- --resume "$SESSION_ID" \
--bare --approval-mode yolo \
--auth-type openai \
--openai-api-key test \
--openai-base-url http://127.0.0.1:9 \
-p continue
If testing a published package instead of the branch source, replace npm run dev -- with that package's qwen binary.
Observed on a build containing the #4286 change:
[API Error: Invalid string length]
The intentionally unreachable --openai-base-url is only a control: smaller sessions that make it past resume and request construction fail with a normal connection error. The bug here is the Invalid string length failure before a controlled oversized-context handling path.
Expected behavior
Qwen Code should not let an oversized resumed history reach JSON/request serialization and fail with a low-level V8 string-length error.
For very large resumed sessions, Qwen Code should do one of the following before serialization:
- compact the model-facing history,
- truncate or summarize older resumed content,
- reject the turn with a clear actionable message, or
- enforce an explicit request-size/history-size budget.
Client information
Client Information
Baseline: qwen@0.15.11
Candidate: local Qwen Code build containing #4286
Node.js: v22.22.0
OS: macOS arm64
NODE_OPTIONS:
V8 heap limit: 4144 MB
Auth: API Key - openai
Base URL in reproduction: http://127.0.0.1:9
Login information
OpenAI-compatible API configuration.
Anything else we need to know?
Local comparison with synthetic alternating user/model messages, each carrying a 1 MiB text part:
| Fixture |
qwen@0.15.11 baseline |
build containing #4286 |
| 300 MB |
reaches request path |
not retested |
| 400 MB |
FATAL ERROR: Reached heap limit |
reaches request path |
| 500 MB |
heap OOM, native stack includes StructuredClone |
reaches request path |
| 600 MB |
baseline already fails at smaller sizes |
[API Error: Invalid string length] |
| 700/900/1200 MB |
baseline already fails at smaller sizes |
[API Error: Invalid string length] |
This suggests #4286 fixes the immediate deep-clone heap peak, but oversized resumed model history still needs a separate guard before request serialization.
Related:
Suggested labels:
type/bug
category/core
scope/session-management
scope/memory-usage
Oversized resumed history can fail with
Invalid string lengthWhat happened?
While validating the long-session resume OOM work in #4286, I found a separate oversized-resume boundary.
Context:
Out of memory when working with Qwen Code in a session with a local Qwen 3.6 model running with llama.cpp under Linux #4351 reports that
qwen@0.15.11can hit V8 heap OOM after resuming a long session, even with a larger heap.fix(core): replace structuredClone with shallow copy to prevent OOM in long sessions #4286 reduces the full-history
structuredClone()peaks. In local testing with a build containing that change, Qwen Code no longer heap-OOMs on the synthetic 400 MB and 500 MB resumed-history cases that crashqwen@0.15.11.After that improvement, a larger resumed model history still fails before a real model response is possible:
This looks like the next boundary after the clone-OOM fix, not a new root cause introduced by #4286. Once the process survives resume/history cloning, the model-facing history can still be large enough that request serialization tries to build a JavaScript string beyond V8's string-length limit.
On my local Node build:
Reproduction
The reproduction below uses an isolated
QWEN_RUNTIME_DIRand synthetic resumed history. It does not modify the real~/.qwensessions.From the repository root:
Then run a Qwen Code build that contains the #4286 change against the generated session, with default heap settings:
If testing a published package instead of the branch source, replace
npm run dev --with that package'sqwenbinary.Observed on a build containing the #4286 change:
The intentionally unreachable
--openai-base-urlis only a control: smaller sessions that make it past resume and request construction fail with a normal connection error. The bug here is theInvalid string lengthfailure before a controlled oversized-context handling path.Expected behavior
Qwen Code should not let an oversized resumed history reach JSON/request serialization and fail with a low-level V8 string-length error.
For very large resumed sessions, Qwen Code should do one of the following before serialization:
Client information
Client Information
Login information
OpenAI-compatible API configuration.
Anything else we need to know?
Local comparison with synthetic alternating user/model messages, each carrying a 1 MiB text part:
qwen@0.15.11baselineFATAL ERROR: Reached heap limitStructuredClone[API Error: Invalid string length][API Error: Invalid string length]This suggests #4286 fixes the immediate deep-clone heap peak, but oversized resumed model history still needs a separate guard before request serialization.
Related:
structuredClone()heap peak and exposed this next oversized-resume boundarySuggested labels:
type/bugcategory/corescope/session-managementscope/memory-usage