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.
JSON Spike is one of those production issues that hides behind ordinary application code. You build a large object, call JSON.stringify or JSON.parse on the main thread, and for that whole stretch the event loop cannot run timers, I/O callbacks, or new HTTP work. Average CPU may not look scary, but users feel freezes and health checks can fail.
In this blog, we’ll walk through how JSON Spike 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 ‘JSON Spike’ in Node.js?
Node.js runs your application on a single main thread. That thread also runs the event loop, so timers, I/O callbacks, and HTTP responses all depend on it. When your code calls `JSON.stringify` or `JSON.parse` on a large object, that work runs to completion on the same thread. There is no background helper that finishes it for you.
While stringify or parse is still running, new requests may already be accepted by the OS, but your JavaScript cannot handle them yet. Timers fire late. Health checks can time out. From the outside, it looks like a short freeze, even though the process is still alive. In yCrash, you usually see that as Event Loop Lag climbing into hundreds of milliseconds or more, and in the CPU Profiler the hot method is often listed as (anonymous) if the work sat inside an unnamed `setInterval` or `setTimeout` callback.
What Causes ‘JSON Spike’ in Node.js?
Here is the usual list of causes for the ‘JSON Spike’ problem.
- Large payloads on the request path: Stringifying or parsing multi-megabyte API bodies, cache blobs, or log batches on the main thread.
- Repeated stringify/parse in a loop: Building a big object and running `JSON.stringify/JSON.parse` more than once in the same tick.
- Timer-driven sync work: A `setInterval` or `setTimeout` callback that does heavy JSON work every few hundred milliseconds.
- Deep or wide object graphs: Objects with tens of thousands of keys or nested arrays that take a long time to walk.
- Logging and debugging leftovers: Serializing huge objects to logs or debug output in hot paths.
How to Simulate JSON Spike in Node.js
To understand how JSON Spike appears in the diagnostic data, let’s reproduce the issue using a sample Node.js application.
The following program wakes every 500 ms, builds an object with 10,000 items, then runs JSON.stringify and JSON.parse twice on that object. All of that happens on the main thread inside an anonymous setInterval callback.
``` JS Code (json-spike.js)class JSONSpikeDemo { constructor() { this.isRunning = false; this.intervalId = null; this.spikeCount = 0; } start() { if (this.isRunning) return; this.isRunning = true; console.log('Starting JSON spike simulation...'); // Anonymous setInterval callback — shows up as (anonymous) in the CPU profile. this.intervalId = setInterval(() => { const largeObj = { data: [] }; for (let i = 0; i < 10000; i++) { largeObj.data.push({ id: i, name: `item_${i}`, values: Array(100).fill(null).map(() => Math.random()), metadata: { created: Date.now(), tags: ['test', 'chaos', 'json'] } }); } for (let i = 0; i < 2; i++) { const jsonStr = JSON.stringify(largeObj); JSON.parse(jsonStr); } this.spikeCount++; console.log(`JSON spike: batch ${this.spikeCount} completed`); }, 500); } stop() { clearInterval(this.intervalId); this.isRunning = false; this.spikeCount = 0; }}module.exports = new JSONSpikeDemo();```
How to Capture Diagnostic Data for Troubleshooting JSON Spike
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.
Analyzing JSON Spike Using the Diagnostic Data
In this section, we’ll see how to analyze a JSON Spike 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 Event Loop Lag and the CPU profile reports to find the code that is blocking the main thread.
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, the root cause is a blocked event loop with lag up to about 1167 ms. The overview also mentions a resource leak (active handles and requests rising). That handle growth is secondary here; the stall is what made the app feel frozen.
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 2 that matter most for a JSON Spike are:
- Event loop blocked
- Hot method
The duplicate ms package warning and the small active-handle growth warning do not explain the freeze. You can set them aside for this investigation.
Step 3: Confirm the stall in Event Loop Lag
Open the Node.js Internals report from the left navigation as shown in the screenshot below. In the top section of this report called “Observations”, you will find all the problems detected in this report. Now let’s click the “detail here” hyperlink present at the end of the Event Loop blocked problem statement. This will take you to the Event Loop Lag section.

Fig: Event Loop Lag in Node.js Internals report
As you can see in the above graph, the lag stays near about 1000–1200 ms for most of the window, with Fatal Spike rows such as 1116 ms, 1038 ms, 1034 ms, and 1095 ms. The worst sample on the RCA Summary is 1167 ms. That shape fits a timer that keeps doing heavy synchronous JSON work on every tick.
Event Loop Lag does not name the function. To find out which code burned that time, we will review the CPU Profiling report next.
Step 4: Use CPU Profiling to close the investigation
In the CPU Profiler report page, yCrash aggregates the V8 sampling profile into a Function Explorer table (hot methods ranked by self CPU %). It provides an option to inspect the hot methods to do a deep diagnosis.
Open the CPU Profiler report page from the left navigation and scroll to the Function Explorer section.

Fig: Function Explorer in CPU Profiler report
The function explorer confirms what we saw on the RCA Summary page: (anonymous) used 94.69% Self CPU across about 26,576 calls on one thread. `listOnTimeout` shows high Total CPU but almost no Self CPU, which means it is the parent timer path waiting on that anonymous callback.
For deep diagnosis, let’s click on the “Inspect” button from the same row.

Fig: Inspector view of (anonymous)
As you can see in the Inspect view above, (anonymous) sits at the top of the parent chain. Under it, you can see `listOnTimeout` and `processTimers`. That matches the unnamed `setInterval` callback in `json-spike.js`, the one that builds the large object and runs `JSON.stringify/JSON.parse`.
This is useful because the CPU Profiler doesn’t just tell you that the event loop was blocked. It shows that the event loop was blocked by synchronous JSON work on the main thread inside a timer callback, not by waiting on I/O or by a worker pool.
How to Fix JSON Spike in Node.js
The following are the potential solutions for this issue:
- Avoid giant `JSON.stringify/JSON.parse` on the request path. You can stream, paginate, or shrink the payload first.
- Move heavy JSON transforms off the main thread to worker threads or a bounded worker pool when you cannot shrink the work.
- Break large serialize/parse jobs into smaller ones and use `setImmediate()` to give timers, I/O, and other events a chance to run.
- Give hot callbacks a function name so the CPU Profiler is easier to read next time.
- Watch Event Loop Lag in production, so sync JSON regressions show up early.
Conclusion
Diagnosing and identifying the root cause of JSON Spike can be challenging when average CPU looks only moderately busy and the process still appears healthy. In this example, the diagnostic data was captured using the yc-360 script and analyzed using yCrash server, which helped correlate Event Loop Lag with a hot anonymous timer callback.
The analysis traced the issue to synchronous JSON.stringify/JSON.parse work inside an unnamed setInterval callback in json-spike.js. By examining the RCA Summary, the Event Loop Lag chart, and the CPU Profiler Inspect view, we could connect the Fatal 1167 ms stall to that path.
Shrinking the payload, moving heavy JSON work off the main thread, or chunking it so the event loop can breathe addresses the underlying problem and helps prevent the issue from recurring.
