HighVulnerability

Critical Node.js async_hooks flaw enables unrecoverable server crashes (CVE-2025-59466)

CVE-2025-59466 can crash Node.js apps when async_hooks is enabled. A stack overflow in async hook callbacks triggers immediate process termination, bypassing error handlers. Patched in Node.js 20.20.0, 22.22.0, 24.13.0, and 25.3.0.

Evan Mael
Evan Mael
Technology1views
CVE identifierCVE-2025-59466
Failure modeExit code 7
Affected versions20.x, 22.x, 24.x, 25.x
Patched versions20.20.0, 22.22.0, 24.13.0, 25.3.0

A denial of service vulnerability does not need remote code execution to be damaging. For organizations running Node.js at scale, a crash that reliably terminates the process can be enough to trigger real downtime, autoscaling thrash, and incident-response fatigue. That is the core risk behind CVE-2025-59466, a Node.js async_hooks error-handling bug that can turn certain stack overflow conditions into an immediate, unrecoverable process exit. In environments where a single Node.js process serves thousands of requests, the difference between a caught RangeError and a hard exit is the difference between a noisy log entry and an outage.

This issue is especially relevant because modern Node.js stacks increasingly enable async context tracking by default. Observability agents, application performance monitoring tooling, and popular frameworks use AsyncLocalStorage and async_hooks to propagate request context across asynchronous boundaries. The result is uncomfortable: the more "production-grade" your Node.js deployment is, the more likely it is to have the exact feature enabled that makes this crash path possible. Node.js has shipped patched releases, but the project also emphasizes a broader lesson: relying on "recoverable stack exhaustion" is not a safe availability strategy when untrusted input can influence recursion depth or workload shape.

What happened: the technical breakdown of CVE-2025-59466

At the center of CVE-2025-59466 is a subtle but high-impact behavior in Node.js exception handling. In normal circumstances, a stack overflow in JavaScript manifests as a RangeError such as "Maximum call stack size exceeded." Many applications treat that as an application bug or an edge case and rely on standard defensive patterns like try-catch and uncaught exception handlers to ensure the process stays alive. The vulnerability appears when async_hooks is enabled and the stack overflow occurs while Node.js is executing internal hook callbacks. In that situation, Node.js can treat the overflow as a fatal condition and terminate the process immediately, bypassing handlers that would otherwise allow graceful recovery.

Operationally, that creates a practical DoS path. If an attacker can trigger code paths that cause deep recursion and repeatedly allocate asynchronous context, they may be able to force the process into a crash loop. This is not a "one packet kills the server" story for every Node.js deployment, but it is a realistic risk in the environments that matter: public-facing web services with complex request handling, server-rendered applications, and any stack where instrumentation is always on. A single hard exit can cascade into repeated restarts, especially in orchestrated environments where health checks will continuously bring the service back up only to have it crash again under the same traffic pattern.

Node.js describes the fix in pragmatic terms: detect stack overflow errors inside the fatal error path and re-throw them to user code rather than treating them as unrecoverable. That restores expected behavior for try-catch and aligns the runtime's failure mode across cases with and without async_hooks enabled. However, the Node.js project is explicit that this is not a magic shield against stack exhaustion. Stack space exhaustion remains an availability hazard with best-effort behavior, and services should be engineered to avoid unbounded recursion when request input or attacker-controlled conditions can influence execution depth.

Why this hits React, Next.js, and APM users harder than expected

This vulnerability is not only about developers who explicitly call async_hooks.createHook. The more important detail is how widely AsyncLocalStorage is used as a building block. AsyncLocalStorage sits on top of async_hooks and is commonly used to store request-level context such as request IDs, authentication state, tracing spans, cookies, or headers. That "context propagation" is now a default pattern in modern server frameworks and observability ecosystems because it makes logs, traces, and metrics far more useful during troubleshooting.

React Server Components and Next.js are often called out because they depend on request context tracking in server environments. In practice, that means that high-level application code can inherit low-level behavior: you may never consciously enable async_hooks, but your framework and instrumentation do it for you. The risk profile changes in two ways. First, the vulnerable crash path becomes reachable in ordinary request processing, not only in niche internal scripts. Second, the attack surface becomes "anything that can cause deep recursion or pathological execution," which can include complex input payloads, deeply nested route patterns, or application-specific parsing behavior.

Observability tooling increases the exposure further. APM agents and tracing libraries often enable hooks globally to capture asynchronous lifecycle events across the entire process. That is a deliberate tradeoff: more visibility in exchange for additional runtime overhead. CVE-2025-59466 adds a second tradeoff to consider: hook-enabled runtime behavior can alter how certain failures are handled, and in this case it can transform a catchable error into a hard exit. For platform teams, this is the kind of incident that justifies treating observability agents as part of the application's threat model. It is not about blaming telemetry, but about acknowledging that "always-on hooks" can affect stability in edge cases that attackers may try to trigger.

Exploitability and real-world impact: when does this become a DoS issue?

The correct question for IT and security teams is not "is this remote exploitable in theory," but "can untrusted input influence the conditions that trigger the crash." CVE-2025-59466 is not a universal remote kill switch. It depends on stack overflow conditions, which typically require deep recursion and a workload shape that pushes execution to the point of stack exhaustion. Many well-structured services avoid deep recursion entirely, or bound recursion depth in places where attacker input can influence it.

But modern application stacks are rarely clean-room designs. Real production Node.js services frequently combine user-controlled inputs with libraries that were written for correctness and speed, not for adversarial inputs. Some code paths still use recursion for convenience, especially for traversing nested structures, rendering component trees, or walking AST-like representations. If that recursion depth can be increased by user input or by attacker-controlled traffic patterns, the preconditions become more plausible. The impact then is straightforward: a hard crash terminates the process, and repeated crashes can produce sustained downtime.

Autoscaling can also make things worse: the platform may respond to failed requests by scaling out, which increases cost and can propagate instability across dependencies. This is how "a medium severity CVE" becomes a high-severity operational incident in the real world.

Node.js has shipped patched releases across active release lines. The most defensible posture is to upgrade quickly, then treat the remaining risk as an engineering problem: do not assume stack exhaustion is safe, and do not treat a "recoverable RangeError" as a security boundary. If your service has any user-influenced recursion, you should address it even after upgrading.

How organizations can respond: patching, mitigations, and safe engineering choices

Patching is the primary fix. Node.js released security updates across multiple active lines, and administrators should align their runtime versions accordingly. For enterprise environments, that means updating base images, rebuilding containers, and ensuring your CI pipeline does not pin older runtime versions. In many organizations, the actual delay is not the patch itself but dependency inertia: teams hesitate to upgrade the runtime because of build tooling, native dependencies, or brittle integration tests. This is one of those cases where the business case is clear. A crash-loop DoS is not theoretical risk, it is measurable downtime risk.

If you cannot upgrade immediately, mitigations should focus on reducing the likelihood of untrusted input causing deep recursion, especially when promises and async context are involved. Practically, that means identifying recursion hotspots that can be influenced by inbound requests. Replace recursion with iterative approaches when possible, implement hard limits for nested structures, and validate payload shapes early. You should also review observability and instrumentation defaults. If an APM agent enables hooks globally, consider whether it can be configured to reduce risky behavior in the short term, with the understanding that disabling hooks can degrade tracing fidelity.

That inventory is your incident response accelerant. If you ever see repeated Node.js exits with a consistent signature, you can quickly determine whether CVE-2025-59466-like behavior is involved.

Closing thoughts

CVE-2025-59466 is a strong example of how "availability bugs" become security issues at internet scale. When a runtime converts a catchable stack overflow into a hard process exit under common production features like async context tracking, attackers do not need sophisticated payloads to cause pain—they only need a reliable crash trigger. The immediate action is to upgrade to patched Node.js versions across active release lines, but the longer-term lesson is engineering discipline: bound recursion, validate untrusted input early, and treat observability features as part of the stability threat model. If your business depends on Node.js uptime, this is the kind of issue that belongs in both your patch SLA and your reliability backlog.

Frequently Asked Questions

It can be, but it depends on whether an attacker can trigger execution patterns that cause deep recursion and stack exhaustion while async context tracking is enabled. Many applications are not vulnerable in practice, but services with user-influenced recursion and always-on instrumentation should treat it as a realistic DoS risk. The safest approach is to upgrade and still harden recursion and input bounds.

The issue appears when the stack overflow occurs while Node.js is executing internal async hook callback machinery. In that situation, the runtime can treat the overflow as a fatal condition and terminate the process rather than letting error handlers catch the RangeError. The patch aims to restore catchable behavior for stack overflow errors in this path.

These frameworks rely on AsyncLocalStorage to track request context in server environments, which is built on async context tracking. That increases the chance that the relevant mechanisms are enabled in production, even if developers never explicitly turned them on. The actual exploitability still depends on application-specific recursion and workload patterns.

Look for sudden increases in Node.js process exits that coincide with traffic spikes or specific routes, especially if logs indicate stack overflow conditions. In Kubernetes, watch for CrashLoopBackOff patterns that repeat under specific request types. Treat deterministic crash loops as potential exploit indicators, not only as "bugs."

Reduce exposure by bounding recursion depth in request paths and validating payload shapes early. Review APM agent settings if hooks are globally enabled, and consider temporary configuration changes that reduce risky execution paths. These mitigations reduce likelihood but do not replace patching.

The patch restores catchability for stack overflow errors in the affected path, but Node.js still warns that stack exhaustion behavior is best-effort and should not be relied upon as an availability or security guarantee. Engineering controls like recursion limits remain necessary for high-availability services.

Incident Summary

Type
Vulnerability
Severity
High
Industry
Technology
Threat Actor
Unconfirmed
Target
Node.js production applications using AsyncLocalStorage, Next.js/React Server Components, and APM instrumentation
Published
Jan 14, 2026

Comments

Want to join the discussion?

Create an account to unlock exclusive member content, save your favorite articles, and join our community of IT professionals.

Sign in