Skip to content

Worker with large inline source map aborts process in V8 HandleDebugMagicComments #64155

Open

Description

@mathieudutour

Version

v26.4.0

Platform

macOS 15.x arm64

Subsystem

worker_threads

What steps will reproduce the bug?

Create a worker entry file with a small amount of executable JavaScript and a very large inline sourceMappingURL=data:... comment, then load it in a worker with a constrained heap.

Minimal repro:

const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { Worker } = require("node:worker_threads");

const dir = fs.mkdtempSync(path.join(os.tmpdir(), "node-inline-sourcemap-oom-"));

const workerPath = path.join(dir, "worker.cjs");

const source = [
  'const { parentPort } = require("node:worker_threads");',
  'parentPort.postMessage("loaded");',
  'parentPort.close();',
  "",
  "//# sourceMappingURL=data:application/json;base64,",
  "A".repeat(64 * 1024 * 1024),
  "",
].join("\n");

fs.writeFileSync(workerPath, source);

const worker = new Worker(workerPath, {
  resourceLimits: {
    maxOldGenerationSizeMb: 16,
  },
});

worker.on("message", console.log);

worker.on("error", console.error);

worker.on("exit", (code) => {
  console.log("exit", code);
  fs.rmSync(dir, { recursive: true, force: true });
});

Run: node repro.js

How often does it reproduce? Is there a required condition?

It reproduces consistently when the inline sourcemap comment is large enough relative to the worker heap limit.

For comparison, these do not reproduce the same fatal abort:

  1. Keeping the same large payload in an external worker.cjs.map file and using //# sourceMappingURL=worker.cjs.map.
  2. Putting a similarly large payload in a normal non-sourceMappingURL comment.

What is the expected behavior? Why is that the expected behavior?

The worker should fail gracefully, for example by emitting an error event or exiting with a worker failure, without aborting the entire Node.js process.

Ideally, parsing sourceMappingURL / debug magic comments should not internalize an arbitrarily large data URL into V8 old space during module compilation.

What do you see instead?

The whole Node process aborts with an out-of-memory fatal error before the worker can load the module.

Example stack from Node v26.4.0:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
...
v8::internal::FactoryBase<v8::internal::Factory>::AllocateRawOneByteInternalizedString
v8::internal::FactoryBase<v8::internal::Factory>::NewOneByteInternalizedString
v8::internal::StringTable::LookupKey
v8::internal::FactoryBase<v8::internal::Factory>::InternalizeString
void v8::internal::Parser::HandleDebugMagicComments
v8::internal::Parser::ParseProgram
v8::internal::parsing::ParseProgram
v8::internal::Compiler::GetWrappedFunction
v8::ScriptCompiler::CompileFunction
node::contextify::CompileFunctionForCJSLoader
node::worker::Worker::Run

Additional information

No response

Activity

  1. Archkon commented on Jul 3, 2026

    @Archkon
  2. eliau2005 commented on Sep 24, 2026

    @eliau2005
    Contributor

    I’m looking into this from the V8 side, since the OOM occurs in V8’s parsing/internalization path. I’ll work on a V8 fix and regression test first, then bring the fix back to Node.js.

  3. eliau2005 commented on Sep 27, 2026

    @eliau2005
    Contributor

    I’m working on a V8 fix for this issue.

    The fix is now under review upstream:
    https://chromium-review.googlesource.com/c/v8/v8/+/8472768

    V8 bug tracking the issue:
    https://issues.chromium.org/issues/566467883

    One clarification: the posted reproduction puts the large payload on the next line, making it a large identifier. The reported CALL_AND_RETRY_LAST failure is reproduced with the large sourceMappingURL value on the same line as the magic comment.

    The fix covers sourceMappingURL, sourceURL, and debugId, including background/streaming compilation.

  4. eliau2005 commented on Oct 13, 2026

    @eliau2005
    Contributor

    The V8 fix has landed upstream (v8/v8@8dcf14f490c2), and I've opened a backport to Node.js main: #66707

    With the fix, the magic comment values are substrings of the script source, so a worker with a large inline sourceMappingURL (on the same line as the comment) now loads normally instead of aborting the process. The PR adds a regression test based on the repro here.

    One limitation: when the script is loaded from the compile cache (NODE_COMPILE_CACHE), these values are still created as full strings on the heap; fixing that needs a code cache format change in V8 and is left for a follow-up.

    Backports to the release lines will follow once this lands on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    v8 engineIssues and PRs related to the V8 dependency.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions