Skip to content

oxc-parser raw transfer allocates ~6 GiB ArrayBuffer per parse → RangeError: Array buffer allocation failed on Windows #23759

Description

@wainwright1000

Summary

oxc-parser's raw-transfer parse path allocates an ArrayBuffer of BLOCK_SIZE + BLOCK_ALIGN6 GiB for every parse buffer. On Linux this is lazily overcommitted and harmless, but on Windows (no overcommit) the full ~6 GiB is charged against the system commit limit (RAM + pagefile) at allocation time. Under ordinary memory pressure this throws:

RangeError: Array buffer allocation failed
    at createBuffer (.../oxc-parser/src-js/raw-transfer/common.js:294)
    at prepareRaw (.../raw-transfer/common.js)
    at parseSyncRaw (.../raw-transfer/eager.js)
    at parseSync (.../oxc-parser/src-js/index.js)

Where

src-js/generated/constants.js: BLOCK_SIZE = 2147483632 (~2 GiB) and BLOCK_ALIGN = 4294967296 (4 GiB); createBuffer() does new ArrayBuffer(BLOCK_SIZE + BLOCK_ALIGN) (~6 GiB). Confirmed still present in the latest release, 0.137.0; first hit on 0.135.0.

Reproduction

Windows, Node ≥ 22 (so raw transfer is enabled), when system commit-free is below ~6 GiB:

const { parseSync } = require("oxc-parser");
parseSync("x.ts", "const x = 1;", { experimentalRawTransfer: true });  // RangeError: Array buffer allocation failed
parseSync("x.ts", "const x = 1;", { experimentalRawTransfer: false }); // OK

In the wild it surfaces via knip, which enables experimentalRawTransfer on Node ≥ 22: knip crashes intermittently on Windows when other processes have consumed commit headroom, while passing when run in isolation (more commit-free).

Environment

Windows, Node v24.15.0, oxc-parser 0.135.0 and 0.137.0. 24 GB RAM, a fixed 16 GB pagefile (commit limit ~40 GB); fails when commit-free drops below ~6 GiB.

Relationship to existing issues

This appears to be the JS-binding sibling of the Rust fixed-size-allocator OOM in #19395 / #20063, which was fixed for the Rust path by switching to VirtualAlloc in #22124 (oxc 0.131.0). That fix did not touch src-js/raw-transfer/, so the Node binding still reserves the full ~6 GiB eagerly. #20513 ("Revamp allocator") mentions removing the need for raw transfer to hold a 4 GiB allocation per AST — is the Node-side createBuffer reservation covered by that work, or should it be tracked separately?

Possible fixes

Bound/shrink the raw-transfer ArrayBuffer; reserve address space and commit on demand (VirtualAlloc/mmap) for the JS buffer as #22124 did for the Rust pool; or expose an option/env to fall back to the non-raw path on memory-constrained platforms.

Related: #20513, #19395, #20063, #22124.

Metadata

Metadata

Assignees

Labels

Type

Fields

Priority

None yet

Effort

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions