Repository navigation
child_process.exec(...) (and fork, spawn) do not abort with expected signal.reason #47814
Copy link
Copy link
Closed
Labels
abortcontrollerIssues and PRs related to the AbortController and AbortSignal APIs.Issues and PRs related to the AbortController and AbortSignal APIs.child_processIssues and PRs related to the child_process subsystem.Issues and PRs related to the child_process subsystem.
Description
Version
v18.16.0
Platform
Microsoft Windows NT 10.0.19044.0 x64
Subsystem
No response
What steps will reproduce the bug?
- Use Node.js
v18.16.0 - Run this script in the REPL or save as a script and run with Node.js
(async () => { const { spawn } = require('child_process'); const abortController = new AbortController(); setTimeout(() => { abortController.abort(new Error('My custom error')); }, 0); try { await new Promise((resolve, reject) => { const child = spawn('echo', ["foo"], { signal: abortController.signal }); child.on('error', (err) => { reject(err); }); child.on('close', () => { resolve(); }); }); } catch(err) { console.log('err', err); // err AbortError: The operation was aborted // at abortChildProcess (node:child_process:720:27) // at EventTarget.onAbortListener (node:child_process:790:7) // at [nodejs.internal.kHybridDispatch] (node:internal/event_target:737:20) // at EventTarget.dispatchEvent (node:internal/event_target:679:26) // at abortSignal (node:internal/abort_controller:314:10) // at AbortController.abort (node:internal/abort_controller:344:5) // at Timeout._onTimeout (REPL22:6:25) // at listOnTimeout (node:internal/timers:569:17) // at process.processTimers (node:internal/timers:512:7) { // code: 'ABORT_ERR' } })();
- Notice
AbortError: The operation was abortedinstead of our customsignal.reason->Error: My custom error
How often does it reproduce? Is there a required condition?
Consistently reproducible (always)
What is the expected behavior? Why is that the expected behavior?
I expect this to behave like the fetch API where the error thrown is the signal.reason that we aborted with. Example:
(async () => {
const abortController = new AbortController();
setTimeout(() => {
abortController.abort(new Error('My custom error'));
}, 0);
try {
const res = await fetch('https://nodejs.org/', { signal: abortController.signal });
} catch(err) {
console.log('err', err);
// err Error: My custom error
// at Object.fetch (node:internal/deps/undici/undici:11457:11)
// at async REPL17:8:21
}
})();What do you see instead?
It always throws: AbortError: The operation was aborted
Additional information
Related to #43874
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
abortcontrollerIssues and PRs related to the AbortController and AbortSignal APIs.Issues and PRs related to the AbortController and AbortSignal APIs.child_processIssues and PRs related to the child_process subsystem.Issues and PRs related to the child_process subsystem.
Activity
child_process: use signal.reason in child process abort
benjamingr commentedon May 2, 2023
That's a bad API since signals get passed into things the code around it can't predict what exceptions may be thrown. The current behavior is intentional and what
fetchdoes is really unfortunate.MadLittleMods commentedon May 2, 2023
@benjamingr Your idea from #43874 to put the
signal.reasonas theError: causesounds decent. Whatever consistent thing would be nice.It seems like things are converging on throwing
signal.reasongivenfetchand #43874 🤷child_process: use signal.reason in child process abort
child_process: use signal.reason in child process abort
child_process: use signal.reason in child process abort
child_process: use signal.reason in child process abort