Skip to main content
DevOps Insightsguides & answers

Node.js · troubleshooting

JavaScript heap out of memory

Node.js ran out of room in its own memory heap and stopped the process: the job needed more than V8's heap limit allows. Usually a build or test run that is simply big, a leak in a long-running process, or a limit set lower than the work needs.

Reproduced on Node.js 25.9.0, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 9 sections

Before you start

  • The command that failed, and whether it is a one-off job (a build, tests) or a process that runs for hours.

Run this first

step 1 of 2
node -p "Math.round(require('v8').getHeapStatistics().heap_size_limit / 1048576) + ' MB'"

Look for A limit close to what the job needs, or well below the machine's memory

Shows the heap limit Node will use on this machine, with the current NODE_OPTIONS.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

When does it fail?

Cause space

4 of 4 still possible

  • A build or test run needs more than the default limitCommon
  • A long-running process keeps growingCommon
  • One operation loads too much at onceOccasional
  • Something set the limit lower than the work needsOccasional

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    node -p "Math.round(require('v8').getHeapStatistics().heap_size_limit / 1048576) + ' MB'"

    Shows the heap limit Node will use on this machine, with the current NODE_OPTIONS.

    Look for A limit close to what the job needs, or well below the machine's memory

  2. Step 2

    echo "$NODE_OPTIONS"

    Shows any heap setting that applies to every node process here.

    Look for --max-old-space-size or --max-old-space-size-percentage

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

A build or test run needs more than the default limit

Common

Bundlers, type checkers and test runners on a large project can legitimately need more heap than a small CI runner's default gives.

Confirm

node -p "Math.round(require('v8').getHeapStatistics().heap_size_limit / 1048576) + ' MB'"

The limit on the machine that fails, compared with the one that works

Fix

  • Raise the limit for the job, below the memory the machine or container really has: NODE_OPTIONS=--max-old-space-size=3072 npm run build.
  • On Node 22 and 24 you can give a share of memory instead: NODE_OPTIONS=--max-old-space-size-percentage=75. Node 20's documentation does not list it, and Node 18 rejects it as a bad option.
  • Run fewer workers at once (tests, parallel builds); each worker is a process with its own heap.
NODE_OPTIONS=--max-old-space-size=3072 npm run build

NODE_OPTIONS reaches the node processes npm starts: through npm run on Node.js 25.9.0 (npm 11.12.1), the script's node reported 3264 MB, against 4288 MB without it.

A long-running process keeps growing

Common

A server that crashes after hours or days, rather than at once, is usually holding on to objects it no longer needs: a cache with no limit, listeners that are never removed, a growing array. A higher limit only moves the crash later.

Confirm

node --heapsnapshot-near-heap-limit=1 server.js

A Heap…heapsnapshot file written just before the crash; open it in Chrome DevTools' Memory tab and sort by retained size

Fix

  • Find what retains the memory in the snapshot and bound it: a size limit on caches, removing listeners, streaming instead of collecting.
  • Restart the process on a schedule only as a stopgap while you find the leak.

One operation loads too much at once

Occasional

Reading a whole large file, parsing a huge JSON document or building one enormous array needs the whole thing in the heap at the same time.

Confirm

node --heapsnapshot-near-heap-limit=1 your-script.js

The snapshot dominated by one large string, buffer or array

Fix

  • Process the data in pieces: streams for files, line-by-line or streaming parsers for large JSON, pagination for database results.

Something set the limit lower than the work needs

Occasional

A NODE_OPTIONS value in a Dockerfile, CI settings or a shell profile can cap the heap below the default without anyone remembering it is there.

Confirm

echo "$NODE_OPTIONS"

A --max-old-space-size you did not expect

Fix

  • Remove the stale setting, or raise it to fit the job and the memory the machine really has.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

Node.js keeps your program's objects in a heap that has a size limit, separate from how much memory the machine has. When the program needs more than that limit and garbage collection cannot free enough, Node stops with this error. Raising the limit helps when the work is genuinely big; it only delays the crash when something keeps growing.

Words used on this page

Heap
The memory where a JavaScript program's objects live. V8, the engine inside Node.js, manages it and collects garbage in it.
Old space
The part of the heap for objects that survive a while. --max-old-space-size sets its limit; the whole heap is a little larger.
NODE_OPTIONS
An environment variable whose options apply to every node process started with it, including ones that npm or a build tool starts.
What is actually happening

Reproduced on Node.js 25.9.0 by running a loop that keeps allocating, with the limit lowered to 64 MB. Node printed a "Last few GCs" block, then "FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory" and a native stack trace, and exited with code 134 (the process aborted). That code, not 137, tells you Node stopped itself; 137 means the operating system or a container limit killed it.

The default limit depends on the machine. On this 16 GB Mac it was 4288 MB; on the site's 2 GB server, running Node 18.19.1, it was 1004 MB. So a build that passes on a laptop can fail on a small CI runner with no code change at all.

--max-old-space-size sets only the old space, so the heap ends up slightly bigger than the number you give: 1536 gave a limit of 1728 MB here. Node's documentation suggests 1536 on a machine with 2 GiB of memory, to leave room for everything else and avoid swapping.

How to tell this is your problem
WhereWhat you see
ConsoleFATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Console, older NodeFATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Exit code134 (aborted), unlike 137 for a process killed for memory

How to know it is actually fixed

  • The job finishes, and for a long-running process, memory levels off instead of climbing until the crash.

Stopping it happening again

  • Set the heap limit explicitly in CI and containers, below the memory actually available, so a bigger machine elsewhere does not hide the problem.
  • Watch a long-running process's memory over days; a steady climb is a leak long before it is a crash.

Security considerations

  • A heap snapshot contains everything in memory at that moment, which can include tokens and personal data. Treat the file like a secret and delete it when you are done.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it