How to Fix: AsyncLocalStorage accessed in runtime where it is not available
The crash is not random: AsyncLocalStorage is being touched during a runtime path where the current environment does not provide Node.js async context APIs. In apps that mix modern bundlers, server rendering, edge-like execution, and AI or auth libraries, this usually appears when code meant for the Node.js runtime gets evaluated in a browser or edge-compatible context during development.
In the reported reproduction repository, the error after npm run dev strongly suggests that a dependency expecting full Node internals is being imported or initialized in a runtime where that API is unavailable. The fix is usually not to polyfill it, but to make sure the affected code runs only on the server and only under the correct runtime.
Understanding the Root Cause
AsyncLocalStorage is part of Node’s async_hooks module. Libraries use it for request-scoped state, tracing, auth context, caching, and framework internals. The problem happens when one of these conditions is true:
- A server-only package is imported into a client component or browser bundle.
- A route or handler is running in an Edge runtime even though the dependency requires full Node APIs.
- A module performs top-level initialization that touches Node-only features during build or hot reload.
- A framework plugin, auth helper, AI SDK, or database layer is shared across both client and server code paths.
Technically, the error appears because the module graph is evaluated in an environment that does not expose node:async_hooks or does not implement AsyncLocalStorage the same way Node does. Development mode can make this more visible because the bundler eagerly evaluates modules for fast refresh and server/client graph analysis.
If this project uses Next.js or a similar React framework, the most likely root cause is one of the following:
- Importing a server utility from a file marked with
'use client'. - Using an SDK inside shared utility code that gets bundled for both server and client.
- Running an API route, auth callback, or AI generation handler under edge instead of nodejs.
Step-by-Step Solution
The reliable fix is to isolate Node-only logic, force the correct runtime for server handlers, and prevent client bundles from importing server dependencies.
1. Find where the Node-only dependency is imported
Search the codebase for libraries related to auth, AI SDKs, request context, telemetry, or server helpers. Also search for files shared between frontend components and backend handlers.
grep -R "use client\|AsyncLocalStorage\|async_hooks\|openai\|auth\|server" .
Pay special attention to:
lib/utilities imported by both UI and API codeapp/components marked with'use client'- Route handlers such as
app/api/**/route.ts - Middleware and edge functions
2. Move server-only code into a dedicated server module
If a library requires Node runtime support, keep it in a file that is never imported by client components.
// lib/server/song-generator.ts
import 'server-only'
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
export async function generateSong(prompt: string) {
const result = await client.responses.create({
model: 'gpt-4.1-mini',
input: prompt,
})
return result
}
The server-only import helps catch accidental client usage early.
3. Do not import server modules inside client components
If a React component has 'use client', it must not directly import Node-only utilities.
// app/components/song-form.tsx
'use client'
import { useState } from 'react'
export default function SongForm() {
const [prompt, setPrompt] = useState('')
async function submit() {
const res = await fetch('/api/generate-song', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt }),
})
const data = await res.json()
console.log(data)
}
return (
<button onClick={submit}>Generate</button>
)
}
The client should call an API route or server action instead of importing the SDK directly.
4. Force the affected route to use the Node runtime
If the framework supports multiple runtimes, explicitly set nodejs for routes that depend on Node internals.
// app/api/generate-song/route.ts
export const runtime = 'nodejs'
import { NextResponse } from 'next/server'
import { generateSong } from '@/lib/server/song-generator'
export async function POST(req: Request) {
const { prompt } = await req.json()
const result = await generateSong(prompt)
return NextResponse.json({ result })
}
This is one of the most important fixes when the error comes from an Edge runtime mismatch.
5. Keep middleware free of Node-only packages
If the project uses middleware.ts, do not import SDKs, database clients, or anything that may depend on AsyncLocalStorage.
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(req: NextRequest) {
return NextResponse.next()
}
Move heavy logic from middleware into route handlers or server functions running in Node.
6. Check shared config files for accidental top-level execution
A common mistake is creating a singleton client in a shared file that gets imported everywhere.
// Bad: shared across client and server
import SomeSdk from 'some-sdk'
export const sdk = new SomeSdk()
Instead, keep initialization in server-only code:
// Good: server-only module
import 'server-only'
import SomeSdk from 'some-sdk'
let sdkInstance: SomeSdk | null = null
export function getSdk() {
if (!sdkInstance) {
sdkInstance = new SomeSdk()
}
return sdkInstance
}
7. Restart dev server after changing runtime boundaries
Hot reload can preserve stale module state. After moving imports or changing route runtime, fully restart development:
rm -rf .next
npm run dev
If the project uses another build cache, clear that as well.
8. Verify dependency compatibility
Some package versions assume a newer framework runtime model. Update framework and SDK packages together if needed.
npm outdated
npm install next@latest react@latest react-dom@latest
Then update the affected SDK only if its release notes confirm compatibility with your framework version.
Common Edge Cases
- Auth libraries in middleware: Some auth helpers rely on request context APIs that are not valid in edge middleware or client bundles.
- Server actions imported into client helpers: Wrapping server code in a utility file does not make it safe if a client component imports that file.
- Environment variable misuse: A module may branch on missing env vars and fall back to code paths that are evaluated too early.
- Transitive imports: Your component may not import the problematic package directly. A helper imported by a helper can still drag Node-only code into the wrong bundle.
- Monorepo package boundaries: Shared internal packages often leak server dependencies into frontend apps unless exports are split clearly.
- Build-only success, dev-only failure: Development bundlers sometimes analyze and evaluate modules more aggressively, exposing incorrect runtime assumptions earlier.
Practical Debugging Checklist
- Check whether the failing file contains
'use client'. - Check whether the route handler exports
runtime = 'nodejs'. - Ensure SDK initialization lives only in a server module.
- Ensure
middleware.tsimports only edge-safe code. - Search for shared utilities imported by both app components and API routes.
- Clear build cache and restart the dev server.
FAQ
Why does this happen only during npm run dev?
Development mode often evaluates more modules for hot reload, dependency graphing, and error overlays. That makes incorrect server/client boundaries show up immediately, even if production build behavior looks different.
Can I polyfill AsyncLocalStorage in the browser or edge runtime?
Usually, no. If a dependency expects real Node async context behavior, a polyfill is not a reliable fix. The correct solution is to run that code only in a Node.js server runtime.
How do I know which package is causing the issue?
Start from the stack trace, then inspect the first application file above node_modules. From there, trace imports backward until you find a client component, middleware file, or edge route importing a Node-only package. Shared utility files are the most common source.
Final Fix Summary
To resolve AsyncLocalStorage accessed in runtime where it is not available, treat it as a runtime boundary bug. Keep Node-dependent SDKs in server-only modules, call them through API routes or server actions, explicitly use runtime = ‘nodejs’ where required, and never import those modules from client components or edge middleware. In projects like the reported song-generator-ai repository, this approach fixes the root problem instead of masking it.