diff --git a/doc/api/cli.md b/doc/api/cli.md
index 988ded3d679ced..3769d3ab64802d 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -97,8 +97,7 @@ Aborting instead of exiting causes a core file to be generated for post-mortem
analysis using a debugger (such as `lldb`, `gdb`, and `mdb`).
If this flag is passed, the behavior can still be set to not abort through
-[`process.setUncaughtExceptionCaptureCallback()`][] (and through usage of the
-`node:domain` module that uses it).
+[`process.setUncaughtExceptionCaptureCallback()`][].
### `--build-snapshot`
diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md
index 46a83e8ab845f4..6264a72d0cc94d 100644
--- a/doc/api/deprecations.md
+++ b/doc/api/deprecations.md
@@ -717,24 +717,6 @@ Type: Documentation-only
The [`ecdh.setPublicKey()`][] method is now deprecated as its inclusion in the
API is not useful.
-### DEP0032: `node:domain` module
-
-
-
-Type: Documentation-only
-
-The [`domain`][] module is deprecated and should not be used.
-
### DEP0033: `EventEmitter.listenerCount()`
-
-Type: Runtime
-
-Users of `MakeCallback` that add the `domain` property to carry context,
-should start using the `async_context` variant of `MakeCallback` or
-`CallbackScope`, or the high-level `AsyncResource` class.
-
### DEP0098: AsyncHooks embedder `AsyncResource.emitBefore` and `AsyncResource.emitAfter` APIs
-
-
-
-> Stability: 0 - Deprecated
-
-
-
-**This module is pending deprecation.** Once a replacement API has been
-finalized, this module will be fully deprecated. Most developers should
-**not** have cause to use this module. Users who absolutely must have
-the functionality that domains provide may rely on it for the time being
-but should expect to have to migrate to a different solution
-in the future.
-
-Domains provide a way to handle multiple different IO operations as a
-single group. If any of the event emitters or callbacks registered to a
-domain emit an `'error'` event, or throw an error, then the domain object
-will be notified, rather than losing the context of the error in the
-`process.on('uncaughtException')` handler, or causing the program to
-exit immediately with an error code.
-
-## Warning: Don't ignore errors!
-
-
-
-Domain error handlers are not a substitute for closing down a
-process when an error occurs.
-
-By the very nature of how [`throw`][] works in JavaScript, there is almost
-never any way to safely "pick up where it left off", without leaking
-references, or creating some other sort of undefined brittle state.
-
-The safest way to respond to a thrown error is to shut down the
-process. Of course, in a normal web server, there may be many
-open connections, and it is not reasonable to abruptly shut those down
-because an error was triggered by someone else.
-
-The better approach is to send an error response to the request that
-triggered the error, while letting the others finish in their normal
-time, and stop listening for new requests in that worker.
-
-In this way, `domain` usage goes hand-in-hand with the cluster module,
-since the primary process can fork a new worker when a worker
-encounters an error. For Node.js programs that scale to multiple
-machines, the terminating proxy or service registry can take note of
-the failure, and react accordingly.
-
-For example, this is not a good idea:
-
-```js
-// XXX WARNING! BAD IDEA!
-
-const d = require('node:domain').create();
-d.on('error', (er) => {
- // The error won't crash the process, but what it does is worse!
- // Though we've prevented abrupt process restarting, we are leaking
- // a lot of resources if this ever happens.
- // This is no better than process.on('uncaughtException')!
- console.log(`error, but oh well ${er.message}`);
-});
-d.run(() => {
- require('node:http').createServer((req, res) => {
- handleRequest(req, res);
- }).listen(PORT);
-});
-```
-
-By using the context of a domain, and the resilience of separating our
-program into multiple worker processes, we can react more
-appropriately, and handle errors with much greater safety.
-
-```js
-// Much better!
-
-const cluster = require('node:cluster');
-const PORT = +process.env.PORT || 1337;
-
-if (cluster.isPrimary) {
- // A more realistic scenario would have more than 2 workers,
- // and perhaps not put the primary and worker in the same file.
- //
- // It is also possible to get a bit fancier about logging, and
- // implement whatever custom logic is needed to prevent DoS
- // attacks and other bad behavior.
- //
- // See the options in the cluster documentation.
- //
- // The important thing is that the primary does very little,
- // increasing our resilience to unexpected errors.
-
- cluster.fork();
- cluster.fork();
-
- cluster.on('disconnect', (worker) => {
- console.error('disconnect!');
- cluster.fork();
- });
-
-} else {
- // the worker
- //
- // This is where we put our bugs!
-
- const domain = require('node:domain');
-
- // See the cluster documentation for more details about using
- // worker processes to serve requests. How it works, caveats, etc.
-
- const server = require('node:http').createServer((req, res) => {
- const d = domain.create();
- d.on('error', (er) => {
- console.error(`error ${er.stack}`);
-
- // We're in dangerous territory!
- // By definition, something unexpected occurred,
- // which we probably didn't want.
- // Anything can happen now! Be very careful!
-
- try {
- // Make sure we close down within 30 seconds
- const killtimer = setTimeout(() => {
- process.exit(1);
- }, 30000);
- // But don't keep the process open just for that!
- killtimer.unref();
-
- // Stop taking new requests.
- server.close();
-
- // Let the primary know we're dead. This will trigger a
- // 'disconnect' in the cluster primary, and then it will fork
- // a new worker.
- cluster.worker.disconnect();
-
- // Try to send an error to the request that triggered the problem
- res.statusCode = 500;
- res.setHeader('content-type', 'text/plain');
- res.end('Oops, there was a problem!\n');
- } catch (er2) {
- // Oh well, not much we can do at this point.
- console.error(`Error sending 500! ${er2.stack}`);
- }
- });
-
- // Because req and res were created before this domain existed,
- // we need to explicitly add them.
- // See the explanation of implicit vs explicit binding below.
- d.add(req);
- d.add(res);
-
- // Now run the handler function in the domain.
- d.run(() => {
- handleRequest(req, res);
- });
- });
- server.listen(PORT);
-}
-
-// This part is not important. Just an example routing thing.
-// Put fancy application logic here.
-function handleRequest(req, res) {
- switch (req.url) {
- case '/error':
- // We do some async stuff, and then...
- setTimeout(() => {
- // Whoops!
- flerb.bark();
- }, timeout);
- break;
- default:
- res.end('ok');
- }
-}
-```
-
-## Additions to `Error` objects
-
-
-
-Any time an `Error` object is routed through a domain, a few extra fields
-are added to it.
-
-* `error.domain` The domain that first handled the error.
-* `error.domainEmitter` The event emitter that emitted an `'error'` event
- with the error object.
-* `error.domainBound` The callback function which was bound to the
- domain, and passed an error as its first argument.
-* `error.domainThrown` A boolean indicating whether the error was
- thrown, emitted, or passed to a bound callback function.
-
-## Implicit binding
-
-
-
-If domains are in use, then all **new** `EventEmitter` objects (including
-Stream objects, requests, responses, etc.) will be implicitly bound to
-the active domain at the time of their creation.
-
-Additionally, callbacks passed to lowlevel event loop requests (such as
-to `fs.open()`, or other callback-taking methods) will automatically be
-bound to the active domain. If they throw, then the domain will catch
-the error.
-
-In order to prevent excessive memory usage, `Domain` objects themselves
-are not implicitly added as children of the active domain. If they
-were, then it would be too easy to prevent request and response objects
-from being properly garbage collected.
-
-To nest `Domain` objects as children of a parent `Domain` they must be
-explicitly added.
-
-Implicit binding routes thrown errors and `'error'` events to the
-`Domain`'s `'error'` event, but does not register the `EventEmitter` on the
-`Domain`.
-Implicit binding only takes care of thrown errors and `'error'` events.
-
-## Explicit binding
-
-
-
-Sometimes, the domain in use is not the one that ought to be used for a
-specific event emitter. Or, the event emitter could have been created
-in the context of one domain, but ought to instead be bound to some
-other domain.
-
-For example, there could be one domain in use for an HTTP server, but
-perhaps we would like to have a separate domain to use for each request.
-
-That is possible via explicit binding.
-
-```js
-// Create a top-level domain for the server
-const domain = require('node:domain');
-const http = require('node:http');
-const serverDomain = domain.create();
-
-serverDomain.run(() => {
- // Server is created in the scope of serverDomain
- http.createServer((req, res) => {
- // Req and res are also created in the scope of serverDomain
- // however, we'd prefer to have a separate domain for each request.
- // create it first thing, and add req and res to it.
- const reqd = domain.create();
- reqd.add(req);
- reqd.add(res);
- reqd.on('error', (er) => {
- console.error('Error', er, req.url);
- try {
- res.writeHead(500);
- res.end('Error occurred, sorry.');
- } catch (er2) {
- console.error('Error sending 500', er2, req.url);
- }
- });
- }).listen(1337);
-});
-```
-
-## `domain.create()`
-
-* Returns: {Domain}
-
-## Class: `Domain`
-
-* Extends: {EventEmitter}
-
-The `Domain` class encapsulates the functionality of routing errors and
-uncaught exceptions to the active `Domain` object.
-
-To handle the errors that it catches, listen to its `'error'` event.
-
-### `domain.members`
-
-* {Array}
-
-An array of timers and event emitters that have been explicitly added
-to the domain.
-
-### `domain.add(emitter)`
-
-* `emitter` {EventEmitter|Timer} emitter or timer to be added to the domain
-
-Explicitly adds an emitter to the domain. If any event handlers called by
-the emitter throw an error, or if the emitter emits an `'error'` event, it
-will be routed to the domain's `'error'` event, just like with implicit
-binding.
-
-This also works with timers that are returned from [`setInterval()`][] and
-[`setTimeout()`][]. If their callback function throws, it will be caught by
-the domain `'error'` handler.
-
-If the Timer or `EventEmitter` was already bound to a domain, it is removed
-from that one, and bound to this one instead.
-
-### `domain.bind(callback)`
-
-* `callback` {Function} The callback function
-* Returns: {Function} The bound function
-
-The returned function will be a wrapper around the supplied callback
-function. When the returned function is called, any errors that are
-thrown will be routed to the domain's `'error'` event.
-
-```js
-const d = domain.create();
-
-function readSomeFile(filename, cb) {
- fs.readFile(filename, 'utf8', d.bind((er, data) => {
- // If this throws, it will also be passed to the domain.
- return cb(er, data ? JSON.parse(data) : null);
- }));
-}
-
-d.on('error', (er) => {
- // An error occurred somewhere. If we throw it now, it will crash the program
- // with the normal line number and stack message.
-});
-```
-
-### `domain.enter()`
-
-The `enter()` method is plumbing used by the `run()`, `bind()`, and
-`intercept()` methods to set the active domain. It sets `domain.active` and
-`process.domain` to the domain, and implicitly pushes the domain onto the domain
-stack managed by the domain module (see [`domain.exit()`][] for details on the
-domain stack). The call to `enter()` delimits the beginning of a chain of
-asynchronous calls and I/O operations bound to a domain.
-
-Calling `enter()` changes only the active domain, and does not alter the domain
-itself. `enter()` and `exit()` can be called an arbitrary number of times on a
-single domain.
-
-### `domain.exit()`
-
-The `exit()` method exits the current domain, popping it off the domain stack.
-Any time execution is going to switch to the context of a different chain of
-asynchronous calls, it's important to ensure that the current domain is exited.
-The call to `exit()` delimits either the end of or an interruption to the chain
-of asynchronous calls and I/O operations bound to a domain.
-
-If there are multiple, nested domains bound to the current execution context,
-`exit()` will exit any domains nested within this domain.
-
-Calling `exit()` changes only the active domain, and does not alter the domain
-itself. `enter()` and `exit()` can be called an arbitrary number of times on a
-single domain.
-
-### `domain.intercept(callback)`
-
-* `callback` {Function} The callback function
-* Returns: {Function} The intercepted function
-
-This method is almost identical to [`domain.bind(callback)`][]. However, in
-addition to catching thrown errors, it will also intercept [`Error`][]
-objects sent as the first argument to the function.
-
-In this way, the common `if (err) return callback(err);` pattern can be replaced
-with a single error handler in a single place.
-
-```js
-const d = domain.create();
-
-function readSomeFile(filename, cb) {
- fs.readFile(filename, 'utf8', d.intercept((data) => {
- // Note, the first argument is never passed to the
- // callback since it is assumed to be the 'Error' argument
- // and thus intercepted by the domain.
-
- // If this throws, it will also be passed to the domain
- // so the error-handling logic can be moved to the 'error'
- // event on the domain instead of being repeated throughout
- // the program.
- return cb(null, JSON.parse(data));
- }));
-}
-
-d.on('error', (er) => {
- // An error occurred somewhere. If we throw it now, it will crash the program
- // with the normal line number and stack message.
-});
-```
-
-### `domain.remove(emitter)`
-
-* `emitter` {EventEmitter|Timer} emitter or timer to be removed from the domain
-
-The opposite of [`domain.add(emitter)`][]. Removes domain handling from the
-specified emitter.
-
-### `domain.run(fn[, ...args])`
-
-* `fn` {Function}
-* `...args` {any}
-
-Run the supplied function in the context of the domain, implicitly
-binding all event emitters, timers, and lowlevel requests that are
-created in that context. Optionally, arguments can be passed to
-the function.
-
-This is the most basic way to use a domain.
-
-```js
-const domain = require('node:domain');
-const fs = require('node:fs');
-const d = domain.create();
-d.on('error', (er) => {
- console.error('Caught error!', er);
-});
-d.run(() => {
- process.nextTick(() => {
- setTimeout(() => { // Simulating some various async stuff
- fs.open('non-existent file', 'r', (er, fd) => {
- if (er) throw er;
- // proceed...
- });
- }, 100);
- });
-});
-```
-
-In this example, the `d.on('error')` handler will be triggered, rather
-than crashing the program.
-
-## Domains and promises
-
-As of Node.js 8.0.0, the handlers of promises are run inside the domain in
-which the call to `.then()` or `.catch()` itself was made:
-
-```js
-const d1 = domain.create();
-const d2 = domain.create();
-
-let p;
-d1.run(() => {
- p = Promise.resolve(42);
-});
-
-d2.run(() => {
- p.then((v) => {
- // running in d2
- });
-});
-```
-
-A callback may be bound to a specific domain using [`domain.bind(callback)`][]:
-
-```js
-const d1 = domain.create();
-const d2 = domain.create();
-
-let p;
-d1.run(() => {
- p = Promise.resolve(42);
-});
-
-d2.run(() => {
- p.then(p.domain.bind((v) => {
- // running in d1
- }));
-});
-```
-
-Domains will not interfere with the error handling mechanisms for
-promises. In other words, no `'error'` event will be emitted for unhandled
-`Promise` rejections.
-
-[`Error`]: errors.md#class-error
-[`domain.add(emitter)`]: #domainaddemitter
-[`domain.bind(callback)`]: #domainbindcallback
-[`domain.exit()`]: #domainexit
-[`setInterval()`]: timers.md#setintervalcallback-delay-args
-[`setTimeout()`]: timers.md#settimeoutcallback-delay-args
-[`throw`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
diff --git a/doc/api/errors.md b/doc/api/errors.md
index b277a53e66cd6d..f392e200b4dd19 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -104,8 +104,7 @@ pass or fail).
For _all_ [`EventEmitter`][] objects, if an `'error'` event handler is not
provided, the error will be thrown, causing the Node.js process to report an
-uncaught exception and crash unless either: The [`domain`][domains] module is
-used appropriately or a handler has been registered for the
+uncaught exception and crash unless a handler has been registered for the
[`'uncaughtException'`][] event.
```js
@@ -298,6 +297,7 @@ console.log(symptom);
// at [_onLine] [as _onLine] (node:internal/readline/interface:425:12)
// at [_line] [as _line] (node:internal/readline/interface:886:18)
```
+
### `error.code`
@@ -1213,25 +1213,6 @@ ongoing asynchronous operations.
`c-ares` failed to set the DNS server.
-
-
-### `ERR_DOMAIN_CALLBACK_NOT_AVAILABLE`
-
-The `node:domain` module was not usable since it could not establish the
-required error handling hooks, because
-[`process.setUncaughtExceptionCaptureCallback()`][] had been called at an
-earlier point in time.
-
-
-
-### `ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`
-
-[`process.setUncaughtExceptionCaptureCallback()`][] could not be called
-because the `node:domain` module has been loaded at an earlier point in time.
-
-The stack trace is extended to include the point in time at which the
-`node:domain` module had been loaded.
-
### `ERR_DUPLICATE_STARTUP_SNAPSHOT_MAIN_FUNCTION`
@@ -3615,7 +3596,6 @@ The native call from `process.cpuUsage` could not be processed.
[crypto digest algorithm]: crypto.md#cryptogethashes
[debugger]: debugger.md
[define a custom subpath]: packages.md#subpath-exports
-[domains]: domain.md
[event emitter-based]: events.md#class-eventemitter
[file descriptors]: https://en.wikipedia.org/wiki/File_descriptor
[policy]: permissions.md#policies
diff --git a/doc/api/events.md b/doc/api/events.md
index 46bc97fb80476f..4ee3d76b838b53 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -156,9 +156,6 @@ myEmitter.emit('error', new Error('whoops!'));
// Throws and crashes Node.js
```
-To guard against crashing the Node.js process the [`domain`][] module can be
-used. (Note, however, that the `node:domain` module is deprecated.)
-
As a best practice, listeners should always be added for the `'error'` events.
```js
@@ -2158,7 +2155,6 @@ to the `EventTarget`.
[`EventTarget` Web API]: https://dom.spec.whatwg.org/#eventtarget
[`EventTarget` error handling]: #eventtarget-error-handling
[`Event` Web API]: https://dom.spec.whatwg.org/#event
-[`domain`]: domain.md
[`emitter.listenerCount()`]: #emitterlistenercounteventname
[`emitter.removeListener()`]: #emitterremovelistenereventname-listener
[`emitter.setMaxListeners(n)`]: #emittersetmaxlistenersn
diff --git a/doc/api/index.md b/doc/api/index.md
index 9c35550f5daf81..f29fdddb611bf0 100644
--- a/doc/api/index.md
+++ b/doc/api/index.md
@@ -27,7 +27,6 @@
* [Deprecated APIs](deprecations.md)
* [Diagnostics Channel](diagnostics_channel.md)
* [DNS](dns.md)
-* [Domain](domain.md)
* [Errors](errors.md)
* [Events](events.md)
* [File system](fs.md)
diff --git a/doc/api/process.md b/doc/api/process.md
index 80fa2f826de93a..308d3a6fa3f2c8 100644
--- a/doc/api/process.md
+++ b/doc/api/process.md
@@ -3444,9 +3444,6 @@ To unset the capture function,
method with a non-`null` argument while another capture function is set will
throw an error.
-Using this function is mutually exclusive with using the deprecated
-[`domain`][] built-in module.
-
## `process.stderr`
* {Stream}
@@ -3819,8 +3816,8 @@ cases:
* `7` **Internal Exception Handler Run-Time Failure**: There was an
uncaught exception, and the internal fatal exception handler
function itself threw an error while attempting to handle it. This
- can happen, for example, if an [`'uncaughtException'`][] or
- `domain.on('error')` handler throws an error.
+ can happen, for example, if an [`'uncaughtException'`][] handler
+ throws an error.
* `8`: Unused. In previous versions of Node.js, exit code 8 sometimes
indicated an uncaught exception.
* `9` **Invalid Argument**: Either an unknown option was specified,
@@ -3873,7 +3870,6 @@ cases:
[`Worker` constructor]: worker_threads.md#new-workerfilename-options
[`console.error()`]: console.md#consoleerrordata-args
[`console.log()`]: console.md#consolelogdata-args
-[`domain`]: domain.md
[`net.Server`]: net.md#class-netserver
[`net.Socket`]: net.md#class-netsocket
[`os.constants.dlopen`]: os.md#dlopen-constants
diff --git a/doc/api/repl.md b/doc/api/repl.md
index 1d216c4358fcd4..9ab115ff505ee9 100644
--- a/doc/api/repl.md
+++ b/doc/api/repl.md
@@ -147,39 +147,6 @@ global or scoped variable, the input `fs` will be evaluated on-demand as
> fs.createReadStream('./some/file');
```
-#### Global uncaught exceptions
-
-
-
-The REPL uses the [`domain`][] module to catch all uncaught exceptions for that
-REPL session.
-
-This use of the [`domain`][] module in the REPL has these side effects:
-
-* Uncaught exceptions only emit the [`'uncaughtException'`][] event in the
- standalone REPL. Adding a listener for this event in a REPL within
- another Node.js program results in [`ERR_INVALID_REPL_INPUT`][].
-
- ```js
- const r = repl.start();
-
- r.write('process.on("uncaughtException", () => console.log("Foobar"));\n');
- // Output stream includes:
- // TypeError [ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException`
- // cannot be used in the REPL
-
- r.close();
- ```
-
-* Trying to use [`process.setUncaughtExceptionCaptureCallback()`][] throws
- an [`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`][] error.
-
#### Assignment of the `_` (underscore) variable