Skip to content

Oversized resumed history can fail with Invalid string length #4363

@yiliang114

Description

@yiliang114

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

Metadata

Metadata

Assignees

No one assigned

    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