Skip to content

AbortController/AbortSignal Triggering 'Error' Event in Child Process #46036

Closed
@pwmichaelharris

Description

@pwmichaelharris

Version

v18.9.0

Platform

Linux Mint 20.1

Subsystem

child_process

What steps will reproduce the bug?

  1. Create a child process with an AbortSignal attached.
  2. Abort the child process via AbortSignal before the child process exit event
  3. This will trigger the child process's error event.

The following code will generate the error for spawn, execFile, and exec

const { spawn, exec, execFile } = require("child_process");

const tests = [
    { commandName: "spawn", command: spawn },
    { commandName: "execFile", command: execFile },
    { commandName: "exec", command: exec }
];

(async () => {
  for ( let { commandName, command } of tests ) {
    const abortController = new AbortController();
    
    await new Promise( resolve => {
      //  I used 'ls' but the command does not seem to effect the result
      const child = command( "ls", { signal: abortController.signal } )
        .on( "error", ( err ) =>
            console.log({ event: `${commandName}.error`, killed: child.killed, err }) )

        .on( "exit", ( code, signal ) => {
            console.log({ event: `${commandName}.exit`, code, signal, killed: child.killed });
        } )

        .on( "close", ( code, signal ) => {
            console.log({ event: `${commandName}.close`, code, signal, killed: child.killed });
            resolve();
        } )

        .on( "spawn", () => {
            console.log({ event: `${commandName}.spawn`, killed: child.killed });
//          child.kill( "SIGTERM" );  //  child will NOT cause an 'error' event
            abortController.abort();  //  child will cause an 'error' event
        } );
    } );
  }
})();

How often does it reproduce? Is there a required condition?

This occurs every time, a child process is aborted via AbortSignal and it has not performed it's exit event.

What is the expected behavior?

{ event: 'spawn.spawn', killed: false }
{ event: 'spawn.exit', code: null, signal: 'SIGTERM', killed: true }
{ event: 'spawn.close', code: null, signal: 'SIGTERM', killed: true }
...
Repeated for execFile and exec

What do you see instead?

{ event: 'spawn.spawn', killed: false }
{
  event: 'spawn.error',
  killed: true,
  err: AbortError: The operation was aborted
      at abortChildProcess (node:child_process:706:27)
      at AbortSignal.onAbortListener (node:child_process:776:7)
      at [nodejs.internal.kHybridDispatch] (node:internal/event_target:731:20)
      at AbortSignal.dispatchEvent (node:internal/event_target:673:26)
      at abortSignal (node:internal/abort_controller:292:10)
      at AbortController.abort (node:internal/abort_controller:322:5)
      at ChildProcess.<anonymous> (/index.js:30:29)
      at ChildProcess.emit (node:events:513:28)
      at onSpawnNT (node:internal/child_process:481:8)
      at process.processTicksAndRejections (node:internal/process/task_queues:81:21) {
    code: 'ABORT_ERR'
  }
}
{ event: 'spawn.exit', code: 0, signal: null, killed: true }
{ event: 'spawn.close', code: 0, signal: null, killed: true }
...
Repeated for execFile and exec

Additional information

According to the documentation a child process error event occurs only on:

  • The process could not be spawned, or
  • The process could not be killed, or
  • Sending a message to the child process failed.

https://nodejs.org/api/child_process.html#event-error

None of these conditions are true when the process is aborted via AbortSignal. If the child process is terminated via a child.kill( "SIGTERM" ) it does not trigger the error event.

I am assuming that AbortSignal.abort() is suppose to act like child.kill( "SIGTERM" ) and not that the documentation is out-of-date.

Metadata

Metadata

Assignees

No one assigned

    Labels

    child_processIssues and PRs related to the child_process subsystem.good first issueIssues that are suitable for first-time contributors.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions