Skip to content

Memory leak from retained sandbox timeouts causes stepwise heap growth #95094

Description

@berry95

Link to the code that reproduces this issue

https://github.com/berry95/next-timeout-resource-leak-repro

To Reproduce

  1. Clone the reproduction repository.
  2. Install dependencies with pnpm install.
  3. Build the standalone server with pnpm build.
  4. Start the standalone server with pnpm start:standalone.
  5. In another terminal, run pnpm load.
  6. Observe the reported server heap usage over repeated requests.

The reproduction sends requests to /api/heap?timeouts=500. The Pages Router
middleware.ts schedules one-shot setTimeout(() => {}, 0) callbacks before
the request reaches the API route. The timeout ids are not retained by user code
and clearTimeout() is not called, matching common fire-and-forget timeout
usage.

The timeouts query parameter intentionally amplifies the issue so retained
sandbox timeout resources are easier to observe locally.

Current vs. Expected behavior

Current behavior:

Naturally completed one-shot timeouts are cleared at the underlying Node timer
level, but their numeric ids remain tracked by Next.js's sandbox
TimeoutsManager. The ids are removed only if user code explicitly calls
clearTimeout(id), or when the whole sandbox module context is cleared.

In a long-lived standalone/self-hosted server process, repeated middleware
requests can therefore make the tracked timeout resources grow monotonically.
The production symptom is step-like heap growth over time and, under sustained
traffic, possible OOM.

Expected behavior:

After a one-shot timeout callback runs, the timeout id should be released from
Next.js's sandbox resource manager even when user code does not explicitly call
clearTimeout(id).

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows
Binaries:
  Node: 24.14.1
  npm: 11.11.0
  pnpm: 10.33.0
Relevant Packages:
  next: 16.3.0-canary.50
  react: 19.2.7
  react-dom: 19.2.7
  typescript: 5.9.3
Next.js Config:
  output: standalone

Which area(s) are affected? (Select all that apply)

Middleware, Pages Router, Runtime, Output, Performance

Which stage(s) are affected? (Select all that apply)

next build (local), Other (Deployed)

Additional context

The sandbox resource managers were introduced in #57235 to avoid retaining old
Edge runtime VM contexts during dev HMR by taking ownership of timers and
clearing them when a module context is invalidated. That teardown path is still
needed.

The missing path is natural completion while the module context remains alive:
one-shot timeout callbacks can finish successfully without releasing their ids
from the sandbox manager.

The existing clearTimeout(timeout) inside webSetTimeoutPolyfill works around
the Node.js primitive timer leak described in nodejs/node#53335, but it does not
remove the id from Next.js's own resource manager.

Metadata

Metadata

Assignees

No one assigned

    Labels

    MiddlewareRelated to Next.js Middleware.OutputRelated to the the output configuration option.Pages RouterRelated to Pages Router.PerformanceAnything with regards to Next.js performance.RuntimeRelated to Node.js or Edge Runtime with Next.js.

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions