The Node.js domain module is deprecated and should not be used in new code. This warning appears when you import or use the domain module. Use async_hooks or AsyncResource instead for proper async context tracking.
The domain module was Node.js's original mechanism for handling asynchronous errors and resource cleanup. However, it proved to be fundamentally unreliable in practice, with design limitations that made it unsuitable for modern asynchronous code patterns. Node.js has therefore deprecated the entire domain module and recommends using async_hooks and the AsyncResource API instead. This deprecation warning appears when you require or import the domain module anywhere in your code. While the module still works, it should not be used in new code, and existing code should be migrated to modern alternatives.
Search your codebase for any imports or requires of the domain module:
grep -r "require('domain')" .
grep -r 'require("domain")' .
grep -r "from 'domain'" .
grep -r 'from "domain"' .Note the files and lines where domain is used.
Review the code where domain is used and identify what it's doing:
// Old domain pattern
const domain = require('domain');
const d = domain.create();
d.on('error', (err) => {
// error handling
});
d.run(() => {
// async code
});Common patterns:
- Creating domains for error handling
- Using domain.run() to execute code within a domain context
- Listening to domain 'error' events
For most use cases, replace domain with async_hooks AsyncResource:
// Modern pattern using AsyncResource
const { AsyncResource } = require('async_hooks');
class MyResource extends AsyncResource {
constructor(name) {
super(name);
}
run(callback) {
return this.runInAsyncScope(callback);
}
}
const resource = new MyResource('MyResource');
try {
resource.run(() => {
// async code
});
} catch (err) {
// error handling
}This provides similar context tracking without the deprecated API.
For more complex async context tracking needs, use async_hooks directly:
const async_hooks = require('async_hooks');
const hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
// Track async resource initialization
},
before(asyncId) {
// Code before async callback
},
after(asyncId) {
// Code after async callback
},
destroy(asyncId) {
// Cleanup
},
});
hook.enable();This gives you fine-grained control over async lifecycle management.
Instead of relying on domain error handling, use modern error handling patterns:
// Promise-based (modern)
async function myAsyncFunction() {
try {
// async code
} catch (err) {
// handle error
}
}
// Or with .catch()
Promise.resolve()
.then(() => {
// async code
})
.catch((err) => {
// handle error
});
// Unhandled rejection handler
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason);
});If the domain module is being used by a dependency:
1. Check if there's a newer version that has removed domain usage:
npm outdated
npm update2. If the dependency is unmaintained, consider replacing it with an actively maintained alternative
3. If it's your own dependency, update it internally to use async_hooks instead
4. Run your test suite after updates:
npm testAfter making changes, verify the deprecation warning is gone:
1. Run your application:
node your-app.js2. Look for the DeprecationWarning in the output - it should be gone
3. Run your test suite to ensure functionality still works:
npm test4. Check for any new errors or warnings that may have appeared
Why domain was unreliable: The domain module attempted to track all asynchronous operations within a domain context, but it had several fundamental issues: it couldn't reliably handle all async patterns (especially Promises), it caused performance overhead, and it was difficult to reason about which operations belonged to which domain.
async_hooks limitations: While async_hooks is more reliable than domain, Node.js itself discourages its use in userland code for most cases. It's a low-level API primarily intended for tooling and instrumentation. For application code, prefer structured error handling patterns (try/catch, .catch(), Promise.allSettled()) and consider using async context libraries like AsyncLocalStorage.
AsyncLocalStorage alternative: For the common use case of tracking request context (like in Express middleware), AsyncLocalStorage provides a cleaner API than raw async_hooks:
const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
// Store context
asyncLocalStorage.run(requestContext, () => {
// code runs with context
});
// Retrieve context
const context = asyncLocalStorage.getStore();Node.js versions: The domain module is deprecated in all modern Node.js versions (v4+), but will not be removed for backward compatibility. However, migration to modern patterns is strongly recommended.
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