Found in the post-#933 survey and re-executed today on a Debug build of main, with node v24.4.1 as the oracle.
zig-js caps a constructed string at max_string_bytes (2^26). It added that cap (7bedeab) so that a small input amplified by a number throws RangeError instead of materializing enormous output, and repeat, +, concat and template literals enforce it. Two builtins with the same amplification shape skip it:
| program | node | zig-js |
|---|---|---|
'a'.padStart(2**53 - 1) | RangeError: Invalid string length | grows past 1 GB until killed |
'a'.padEnd(2**31) | RangeError | grows past 1 GB until killed |
'a'.padStart(2**26 + 10).length | 67108874 | 67108874, though 'a'.repeat(2**26 + 1) throws RangeError |
new Array(2**30).join('ab') | RangeError, immediately | still running after 90 s |
node reads element 0 and then throws. With Object.defineProperty(Array.prototype, 0, { get(){ … } }) installed, the getter runs once before the RangeError.
Fix: padStart/padEnd check the target length against the cap before allocating the fill, as repeat does; a result of target code units needs at least target bytes. join and toLocaleString treat the separators still to come as a floor on the result and throw once that floor passes the cap. That reproduces V8's observable order: element 0 is read, then the error is thrown.
Not changed here: output proportional to the data rather than to a numeric argument. [s, s, 'x'].join(''), String.raw, JSON.stringify and Array.prototype.toString can already produce strings over 2^26 when their inputs are that large. Capping them would newly reject outputs that work today, and JavaScriptCore's limit is 2^31-1. Where zig-js's maximum string length should sit is a product decision (raised on #933), not something to settle inside a bug fix.