· 2 min read ·

When Async Hooks Eat Your Stack: The Node.js DoS Disclosure Worth Revisiting

Source: nodejs

Async hooks occupy a strange corner of Node.js. Most developers never touch them directly, but plenty of production code depends on them through layers of abstraction: React Server Components, Next.js per-request context, APM agents from Datadog and New Relic, and anything else that needs to propagate a value through an async call chain without threading it through every function signature.

The January 2026 Node.js advisory put that abstraction layer under a spotlight. The vulnerability is straightforward in concept: when async hooks are involved in processing a request, certain pathological inputs can drive the call stack deep enough that Node.js runs out of stack space in a way it cannot recover from. That makes it a denial-of-service vector.

Why Stack Exhaustion Becomes Unrecoverable

Normal stack overflows in JavaScript are catchable. Write a recursive function that goes too deep, and you get a RangeError: Maximum call stack size exceeded that code higher up can catch and handle.

Async hooks change the picture. The hooks run in C++ internals when async operations are initialized and resolved, which means they execute outside the scope where JavaScript error handlers can intercept. When the stack exhausts in this zone, Node.js terminates the worker or the entire process, depending on your setup. The error is not catchable at the JavaScript level.

Next.js and React Server Components are particularly relevant because they use AsyncLocalStorage heavily, both for their own request context (cookies, headers, caches) and as a substrate other libraries layer on top of. APM agents also attach async hooks to build distributed traces, which means two separate hook consumers are stacking on top of each other in a typical production app.

What Triggers It

The trigger is recursive or deeply nested async operations that each initialize a new async context. Consider a request handler that spawns async work, which in turn spawns more async work, all tracked through AsyncLocalStorage. If an attacker’s input can influence the depth of that tree, they can craft a payload that drives it into the unrecoverable zone.

The mitigations described in the advisory are layered. Node.js itself gained protections to bound how deeply async resource initialization can nest. Framework-level changes in Next.js limit context propagation depth in hot paths. APM vendors were notified through coordinated disclosure and updated their hook initialization patterns accordingly.

What This Means in Practice

If you run Next.js behind a public endpoint and have not applied the patched versions, this warrants immediate attention. The affected surface is not obscure; it is the standard request handling path that every RSC application exercises on every request.

The broader issue this surfaces is about a documentation gap. Async hook execution happening partially in C++ internals, where JavaScript error handling cannot reach, is not a well-understood property relative to how widely the mechanism is now used through AsyncLocalStorage. As more frameworks treat it as a foundational primitive, the security implications of that C++/JS boundary deserve more visibility than they currently get in Node.js documentation and framework guides.

Was this interesting?