TL;DR โ Quick Fix
Run your script with a larger heap:
node --max-old-space-size=4096 your-script.js
Or set it via environment variable โ handy for build tools where you can't easily edit the command directly:
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build
Replace 4096 (4 GB) with a value that fits your machine. Common values: 2048, 4096, 8192.
What Causes This Error
Node.js runs on the V8 engine, which caps heap memory to prevent runaway processes. On 64-bit systems the default limit is roughly 1.5 GB; on 32-bit systems, about 512 MB. Exceed that ceiling, and the process dies immediately with:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
The usual suspects:
- Building large front-end projects โ webpack, Next.js, and Create React App are notorious for this
- Processing large files or datasets entirely in memory
- Memory leaks: objects that keep accumulating and never get garbage-collected
- Recursive functions that don't terminate correctly
- Running several heavy operations at the same time
Fix 1: Increase the V8 Heap Limit
Start here. It's a one-liner and usually unblocks you immediately.
Directly in the command
node --max-old-space-size=4096 server.js
Via NODE_OPTIONS (recommended for build scripts)
# Linux / macOS
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build
# Windows (Command Prompt)
set NODE_OPTIONS=--max-old-space-size=4096
npm run build
# Windows (PowerShell)
$env:NODE_OPTIONS="--max-old-space-size=4096"
npm run build
In package.json scripts
{
"scripts": {
"build": "node --max-old-space-size=4096 node_modules/.bin/webpack",
"start": "node --max-old-space-size=4096 server.js"
}
}
Choosing the right value
Aim for about 75% of your available RAM. Check what you have first:
# Linux
free -m
# macOS
sysctl hw.memsize
# Windows
wmic OS get TotalVisibleMemorySize
On an 8 GB machine, --max-old-space-size=6144 is a reasonable ceiling. Don't allocate the full amount โ the OS and other processes need headroom too.
Fix 2: Find and Fix Memory Leaks
Bumping the heap is a band-aid. If the process grows without stopping, it will crash again โ just at a higher number. You need to track down the leak.
Generate a heap snapshot
node --inspect your-script.js
Open chrome://inspect in Chrome, click Open dedicated DevTools for Node, then go to the Memory tab. Take a snapshot, let the app run a while, take another. Look for object types that keep piling up between snapshots โ those are your leak candidates.
Monitor heap usage in real time
const v8 = require('v8');
setInterval(() => {
const stats = v8.getHeapStatistics();
const usedMB = Math.round(stats.used_heap_size / 1024 / 1024);
const totalMB = Math.round(stats.heap_size_limit / 1024 / 1024);
console.log(`Heap: ${usedMB} MB used / ${totalMB} MB limit`);
}, 5000);
Watch the numbers. A healthy process plateaus after warm-up. If usedMB keeps climbing with no ceiling, something is holding references it shouldn't.
Common leak patterns
- Event listeners not removed โ call
emitter.removeListener()oremitter.off()once the listener is no longer needed - Closures capturing large objects โ long-lived callbacks that reference big variables prevent the garbage collector from freeing them
- Unbounded in-memory caches โ swap plain objects or Maps for a size-limited cache like
lru-cache - Unhandled promise rejections โ rejected promises that are never caught can quietly accumulate over time
# Install a lightweight memory profiler
npm install --save-dev clinic
npx clinic heapprofiler -- node your-script.js
Fix 3: Process Data in Streams, Not All at Once
Loading a 500 MB file into a variable is an instant recipe for this crash. Streams read and discard data as they go โ heap usage stays flat:
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('large-file.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) => {
// process one line at a time โ no giant array in memory
processLine(line);
});
When you must work with an array, process it in batches:
async function processInChunks(array, chunkSize, fn) {
for (let i = 0; i < array.length; i += chunkSize) {
const chunk = array.slice(i, i + chunkSize);
await fn(chunk);
}
}
A chunk size of 100โ500 items is a good starting point โ enough to keep throughput high without spiking memory.
Verify the Fix
Confirm the new limit is actually in effect before re-running your build:
node -e "const v8 = require('v8'); console.log(v8.getHeapStatistics().heap_size_limit / 1024 / 1024 + ' MB');"
# After setting --max-old-space-size=4096, expected output:
# 4096 MB
Re-run the command that was crashing. If it still fails at the higher limit, the root cause is a memory leak โ go back to Fix 2.
Further Reading
- Node.js CLI options โ
node --help | grep max-old-space-size - V8 heap profiling: Node.js Diagnostics Guide
clinictoolkit for advanced profiling: clinicjs.org

