Production issues in Node.js applications can be challenging to diagnose, especially when different problems exhibit similar symptoms. To make troubleshooting easier, we’re introducing a Node.js Production Troubleshooting Series, where each article focuses on one specific production issue, explores its underlying causes, and demonstrates a practical, step-by-step approach to identifying the root cause.
Similar to Java’s StackOverflowError, Node.js throws a RangeError: Maximum call stack size exceeded when recursive calls exhaust the available call stack. This is one of those production issues that hides behind a process that still looks alive. The main thread may keep answering health checks while a worker burns a core, grows the call stack until it snaps, dies, and gets started again. Average CPU on the host does not look scary. By the time someone opens a profiler, the worker that held the deep stack is often already gone.
In this blog, we’ll walk through how Stack Overflow shows up in a Node.js application, what typically causes it, and how to trace it back to the exact root cause. We’ll also simulate the issue in a sample application, capture diagnostic data as it happens, and analyze it step by step to pinpoint what’s going wrong.
What is ‘RangeError: Maximum call stack size exceeded’ in Node.js?
Every function call in JavaScript pushes a frame on the stack. Recursion is fine when it stops. If there is no base case, or the stop condition never matches the input, frames keep piling up until V8 throws RangeError: Maximum call stack size exceeded.
When that happens on the main thread, the event loop freezes. When it happens in a worker, the main thread can still run. The worker pegs a core, then crashes. The clue is a pegged worker whose call stack is the same function over and over.
What causes ‘RangeError: Maximum call stack size exceeded’ in Node.js?
Here is the usual list of causes for the ‘RangeError: Maximum call stack size exceeded’ problem.
- Missing base case: A recursive function never reaches a stop condition, or the condition is wrong for some inputs.
- Untrusted depth: Walking user JSON, trees, or graphs recursively without a max depth.
- Mutual recursion: Two functions call each other, and the cycle never ends.
- Work hidden in workers: The overflow runs in worker_threads, so the main process stays up, and only the worker dies.
- Crash-and-replace loops: A supervisor starts the same broken worker again after every crash, so the overflow never stops.
How to Simulate RangeError: Maximum call stack size exceeded in Node.js
To understand how maximum call stack size exceeded appears in the diagnostic data, let’s reproduce the issue using a sample Node.js application.
The following program starts five named workers and sends each one into recursion with no base case. When a worker exits, a new one starts right away so the failure stays visible long enough to capture.
``` JS Code (stack-overflow.js)const { Worker } = require('worker_threads');const MAX_WORKERS = 5;let nextWorker = 1;const workers = new Set();function spawnWorker() { const number = nextWorker++; const worker = new Worker(` function infiniteRecursion() { const frameData = new Array(1000); frameData[0] = Date.now(); return infiniteRecursion(); } infiniteRecursion(); `, { eval: true, name: `stack-overflow-worker-${number}` }); workers.add(worker); worker.on('error', () => {}); worker.on('exit', () => { workers.delete(worker); setImmediate(spawnWorker); });}for (let i = 0; i < MAX_WORKERS; i++) { spawnWorker();}```
How to Capture Diagnostic Data for Troubleshooting RangeError: Maximum call stack size exceeded
yCrash is a diagnostic tool that captures runtime performance data from a live Node.js process, including CPU usage, event loop lag, worker thread activity, and call stacks, and turns that data into a report that points to the root cause. To set up yCrash for your Node.js application, follow the steps below:
1. Install yCrash
yCrash is available in both Cloud and On-premises versions. Use one of the options below to get started:
- Cloud Service: Create an account to use the cloud version.
- On-premises: Register for a 14-day trial and install the yCrash application on your local machine or within the organization’s environment.
2. Set up the yCrash Node.js hook first
Please note that this is a one-time setup. The hook must be loaded into your Node.js application before yCrash can capture the Node.js internals data that matters for diagnosis, things like worker thread CPU, event loop lag, method profiling, and call stacks. To set this up, follow the Hook Mode steps here: Node.js Diagnostic Capture. This hook setup adds very minimalistic, almost zero overhead to your application.
3. Launch yc-360 script
Configure and start the yc-360 script to monitor your Node.js process. The full setup and configuration options specific to your deployment are available here: Micro-metrics Monitoring (M3) Mode
When the yc-360 script detects a problem, it captures 360° diagnostic artifacts (GC log, Event loop lags, Worker threads, CPU Profile, Unhandled rejections, Call stack, Active handles, Process, Storage, Kernel, Network, etc.). It uploads them to the yCrash server automatically. yCrash server analyzes the artifacts using advanced pattern recognition and ML algorithms to identify the root cause of the problem.
How to Analyze RangeError: Maximum call stack size exceeded Using the Diagnostic Data
In this section, we’ll see how to analyze a RangeError: Maximum call stack size exceeded using the diagnostic data captured by yCrash. We’ll start with the incident on the calendar dashboard, check the RCA Summary to understand what’s going on, and then drill into Worker Threads and the call stack to find the function with no base case.
Step 1: Open the incident from the yCrash dashboard
When the yc-360 script detects any performance degradation in the Node.js application, it automatically captures 360° diagnostic data and transmits it to the yCrash server. The yCrash server analyses this data and creates an incident in the yCrash dashboard as shown in the screenshot below. Open that incident from the yCrash calendar dashboard to view the reports.

Fig: yCrash dashboard, incident list view
Step 2: Start with the RCA Summary Page
Before diving into details, let’s start with the RCA Summary. yCrash analyzes the Node.js diagnostic data and brings the key findings together in simple language with their severity levels.
In the RCA Summary, we will focus on two sections:
- AI Overview
- Issues in the application and device
AI Overview
At the top of the RCA summary report page, you’ll see an AI Overview section. It gives you an AI-generated summary of the incident, which would make even a non-technical person understand the root cause of the problem. Within the AI Overview section, below the AI summary, you can also click on the ‘Dive Deeper in AI Mode‘ button to open a chat window to ask any question to the AI about this incident.

Fig: RCA Summary highlighting the AI Overview
As shown in the AI Overview above, three workers sit near 128% CPU and the stacks repeat `infiniteRecursion`. That is the Maximum call stack size exceeded problem. The same overview also mentions GC time around 10.05% and a memory leak, but those are not the main issues in this case. The App Memory usage is only around 6%, so memory is not a concern here.
The repeated `infiniteRecursion` calls in the worker threads are the key clue to the RangeError: Maximum call stack size exceeded.
Issues in application and device

Fig: RCA Summary highlighting the issues in application and device
As shown in the above screenshot, several application-level issues are reported. The one that matters most for Maximum call stack size exceeded is:
- Worker pool overloaded
Ignore the rest of this list for this walkthrough:
- the GC Memory Leak banner,
- GC time at 10.05%,
- the 102 ms event-loop stall,
- the duplicate ms package warning, and
- the disk usage warning.
They are noise next to the pegged workers
Step 3: Identify the pegged workers
Open the Node.js Internals report by clicking the “details here” link at the end of Worker pool overloaded problem statement in the “Issue in the Application” section on the RCA page.
This takes you to the Worker Threads section of the Node.js internals report.

Fig: Worker Threads table showing three pegged workers
The table shows which workers are consuming CPU. In this example, three workers are marked Pegged and are using 128.38% of a CPU core, with each worker using around 537 MB RSS:
- stack-overflow-worker-3 (554)
- stack-overflow-worker-1 (552)
- stack-overflow-worker-2 (553)
That is not a thread leak. The count is small. It is also not a generic CPU spike on the main thread. The worker pool is overloaded because these three workers are stuck using CPU while heading toward a stack overflow.
Next, open one of the workers to see what it is doing.
Step 4: Open View Details and read the recursive stack
Click View Details on the hottest worker. Here that is stack-overflow-worker-3 (554), the same ID called out in the RCA finding.

Fig: Call stack repeating infiniteRecursion on stack-overflow-worker-3
The call stack makes the problem clear. The same `infiniteRecursion` function appears repeatedly in the stack, coming from the worker’s eval script. There is no base case, and no other application function appears between the recursive calls.
Worker Method Profiling is empty in this capture. That is expected. The worker is stuck in the recursive call and never returns to its message loop, so it cannot respond to a profiling request.
The CPU usage and captured stack are enough to identify the problem.
How to Fix RangeError: Maximum call stack size exceeded in Node.js
The following are the potential solutions for this issue:
- Add a real base case, and test the inputs that used to walk forever.
- Cap recursion depth for untrusted trees, JSON, or graph walks. Prefer a loop and an explicit stack when depth is unknown.
- If a worker dies with Maximum call stack size exceeded, stop starting the same job again with no retry limit.
- Do not move a broken recursive function into a worker and call it fixed. The isolate still burns a core and then crashes.
Conclusion
Diagnosing and identifying the root cause of RangeError: Maximum call stack size exceeded can be challenging when the main thread still looks healthy. In this example, the diagnostic data was captured using the yc-360 script and analyzed using the yCrash server, which helped correlate three pegged workers with a repeating `infiniteRecursion` stack.
The analysis traced the issue to unbounded recursion inside the workers. The RCA Summary called the saturated pool. View Details showed `infiniteRecursion` with no base case.
The fix is to stop the recursion by adding a proper base case or replacing it with a bounded loop. Simply restarting workers will not fix the underlying problem. They will run into the same issue again.
