This error occurs when attempting to access AsyncLocalStorage data outside of an asynchronous context initialized by run() or enterWith(). The store is only available within the callback scope or in operations properly bound to that context.
AsyncLocalStorage is Node.js's mechanism for maintaining context-specific data across asynchronous operations. When you call asyncLocalStorage.getStore() outside of a run() or enterWith() context, it returns undefined because no store has been established for the current execution context. This error typically manifests when store operations are attempted in callbacks, Promise chains, or async operations that aren't properly bound to the original context.
The most common fix is to ensure all code that uses the store is wrapped in asyncLocalStorage.run(). This establishes the async context:
const { AsyncLocalStorage } = require("node:async_hooks");
const asyncLocalStorage = new AsyncLocalStorage();
asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage.getStore().set("key", "value");
// Code here has access to the store
console.log(asyncLocalStorage.getStore()); // Returns the Map
});The first argument to run() is the store object (can be Map, object, etc.), and the second is the callback containing your code.
Callback-based APIs often lose context. Convert them to Promises using util.promisify():
const { promisify } = require("util");
const asyncLocalStorage = new AsyncLocalStorage();
// Instead of passing a callback that loses context:
asyncLocalStorage.run(new Map(), async () => {
// BAD: Context may be lost
// oldCallbackApi((err, result) => {
// asyncLocalStorage.getStore(); // undefined
// });
// GOOD: Use promisify to preserve context
const promisifiedApi = promisify(oldCallbackApi);
const result = await promisifiedApi();
asyncLocalStorage.getStore(); // Returns the store
});For custom async operations that aren't automatically context-aware, use AsyncResource:
const { AsyncResource } = require("node:async_hooks");
const { AsyncLocalStorage } = require("node:async_hooks");
const asyncLocalStorage = new AsyncLocalStorage();
class CustomAsyncOp extends AsyncResource {
constructor(callback) {
super("CustomAsyncOp");
this.callback = callback;
}
run(data) {
// runInAsyncScope preserves the async context
this.runInAsyncScope(this.callback, null, data);
}
}
asyncLocalStorage.run(new Map(), () => {
new CustomAsyncOp(() => {
console.log(asyncLocalStorage.getStore()); // Context preserved
}).run({ some: "data" });
});For middleware or frameworks where you can't wrap the entire request flow, use enterWith():
const { AsyncLocalStorage } = require("node:async_hooks");
const asyncLocalStorage = new AsyncLocalStorage();
// Express middleware example
app.use((req, res, next) => {
const store = new Map();
store.set("userId", req.user.id);
// enterWith sets the store for the current async context
asyncLocalStorage.enterWith(store);
next(); // Store is available in route handlers
});
app.get("/", (req, res) => {
const store = asyncLocalStorage.getStore();
console.log(store.get("userId")); // Works!
});Add logging to identify where context is lost:
const { AsyncLocalStorage } = require("node:async_hooks");
const asyncLocalStorage = new AsyncLocalStorage();
asyncLocalStorage.run(new Map(), async () => {
console.log("1. In run():", asyncLocalStorage.getStore()); // Should exist
setTimeout(() => {
console.log("2. In setTimeout:", asyncLocalStorage.getStore()); // May be lost
}, 0);
await new Promise((resolve) => {
console.log("3. In Promise constructor:", asyncLocalStorage.getStore()); // Should exist
resolve();
});
console.log("4. After await:", asyncLocalStorage.getStore()); // Should exist
});Run this to pinpoint where the store becomes undefined, then apply the appropriate fix for that operation type.
AsyncLocalStorage is designed to work with native Promises and async/await. Some frameworks or libraries with custom Promise implementations may not properly propagate context. In these cases, you may need to manually manage context using AsyncResource or by wrapping operations. Also note that setTimeout, setInterval, and other timer-based APIs do not automatically preserve AsyncLocalStorage contextโthey execute in a new context. For timer-based operations, either use enterWith() before the timer, or restructure to use async/await with native Promises. When working with worker threads or child processes, each thread has its own AsyncLocalStorage instance; context is not automatically shared across process boundaries.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams