JavaScript Math.max RangeError on Large Arrays: Scan the Values
If Math.max(...values) works on sample data and throws RangeError: Maximum call stack size exceeded on a large array, check how many arguments the call receives. Spread passes each array element as a separate argument. Math.max.apply(null, values) also builds an argument list from the array. A loop can keep each call to two arguments regardless of the input length. MDN’s Math.max reference
Reproduce the error and check the replacement
For an array of JavaScript numbers, start at -Infinity and call Math.max with the current maximum and the next value. The seed lets a negative value win and gives the same empty result as Math.max(). Using Math.max for each comparison also carries NaN through to the result and prefers positive zero when both zero signs appear. A bare > comparison can lose those behaviors. ECMAScript Math.max algorithm
Save the fixture as max-scan.mjs and run node max-scan.mjs. The large array puts its largest value at the last index, so the successful result also checks that the scan reaches the end. The small cases use Object.is to distinguish the two zero signs and compare NaN results.
import assert from 'node:assert/strict';
function maxByScan(values) { let maximum = -Infinity; for (const value of values) { maximum = Math.max(maximum, value); } return maximum;}
const small = [-9, -2, -5];const large = new Array(1_000_000).fill(-9);large[large.length - 1] = -2;
assert.equal(Math.max(...small), -2);assert.throws(() => Math.max(...large), RangeError);assert.throws(() => Math.max.apply(null, large), RangeError);assert.equal(maxByScan(large), -2);
for (const values of [small, [], [-0], [-0, 0], [4, NaN, 7]]) { assert.ok(Object.is(maxByScan(values), Math.max(...values)));}
console.log('spread and apply: RangeError');console.log('scan:', maxByScan(large));console.log('five small-input comparisons passed');Output on macOS arm64 with Node.js 24.13.0 and V8 13.6.233.17-node.37:
spread and apply: RangeErrorscan: -2five small-input comparisons passedOne million elements is a reproduction size for that environment. The argument limit can vary with the engine and available stack. Use the scan when the input size can grow beyond what you control, without relying on a fixed cutoff. This example assumes a dense numeric array. If an empty selection means missing data in your application, define that result at the caller boundary. Choosing an initial value for an empty reduction
References: