SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Architecture / 8 MIN READ

Node.js v26.8.2: waiting for ChildProcess exit, close, and success

Compare exit and stdio closure, define Promise success explicitly, and separate proposed ChildProcess methods from Node.js v26.8.2's documented APIs.

Choose a Promise wait by answering two questions: which lifecycle boundary matters, and what counts as success?

For a spawned Node.js child process, exit reports process termination, while close reports termination and stdio closure. Neither event’s arrival alone establishes a successful exit status. events.once() converts an event wait into a Promise, but rejects if the child emits error while that wait is pending. That rejection is not evidence that stdio has closed. ChildProcess lifecycle · events.once()

Reference release: Node.js v26.8.2. The comparison below uses version-pinned documentation for that release. It does not assume that v26.8.2 is installed in your environment or that you use a particular operating system.

Are closed(), exited(), and succeeded() available?

The v26.8.2 ChildProcess reference does not document closed(), exited(), or succeeded() as public methods. For this reference release, use the documented lifecycle events and events.once() rather than relying on those proposed method names.

Node.js issue #59994, opened on September 24, 2025, requested three distinct contracts:

Proposed method Requested fulfillment Requested rejection v26.8.2 public API status
closed() Fulfill with the child process when close fires Reject if error fires Not documented
exited() Fulfill with the child process when exit fires Reject if error fires Not documented
succeeded() Fulfill with no value when the child closes with exit code zero Reject otherwise Not documented

These are the author’s proposed contracts, not shipped API documentation. September 24, 2025 is the feature-request date, not a release date. The request does not establish availability.

For another installed release, identify its exact version and check each method against that release’s documentation. The v26.8.2 answer is a reference point, not a compatibility determination for an unspecified runtime.

Exit and close mark different boundaries

Node.js v26.8.2 documents these distinctions:

  • exit: the child process has ended, but its stdio streams might still be open.
  • close: the process has ended and the child’s stdio streams have closed. Multiple processes can share stdio streams.
  • Ordering: close follows exit, or follows error if the child failed to spawn.
  • Errors: an error event does not guarantee a subsequent exit event. Lifecycle definitions

When reporting process termination, preserve both event arguments. Ordinary exit supplies a numeric code and a null signal; signal termination supplies a null code and the signal name. Consequently, if (!code) cannot distinguish zero from null. Use explicit comparisons. Exit and close arguments

Compare Promise settlement policies in v26.8.2

Here, once means events.once() from node:events. The first two rows use its documented contract. The third adds an application-defined success policy; it is not an implementation of a documented ChildProcess.succeeded() method. These rows assume no cancellation signal is passed to events.once().

Completion goal Documented event/API Promise settlement policy Failure cases and limits Primary-source citation
Observe process termination once(child, 'exit') Fulfill with [code, signal] when exit arrives; reject if error arrives first A nonzero code or signal does not itself cause rejection. Stdio may remain open. After an error, exit is not guaranteed. ChildProcess, once
Observe termination and stdio closure once(child, 'close') Fulfill with [code, signal] when close arrives; reject if error arrives first A nonzero code or signal does not itself cause rejection. Error rejection can precede closure. ChildProcess, once
Require zero-status completion at close, with immediate error reporting close, events.once(), and an explicit status check Chosen policy: fulfill with no value only when the close wait fulfills with code === 0 and signal === null; reject on another status or if the close wait rejects Spawn and other emitted errors can reject before closure. Nonzero status and signal termination fail this policy. ChildProcess, once

The distinction is event fulfillment versus application success. events.once() returns the event’s argument array; it does not interpret the exit code. Its fulfillment value also differs from the child-process object requested for the proposed closed() and exited() methods. once contract · Proposed contracts

Add further acceptance checks if your task requires more than a zero process status—for example, validation of the output your application needs.

Keep failure reporting separate from closure observation

A ChildProcess error can report failure to spawn, failure to kill the process, failure to send a message, or abortion through the child-process signal option. It is not exclusively a spawn-failure notification. Node.js warns that handlers observing both error and exit must guard against invoking shared handler logic multiple times. Error cases and safeguard

For a wrapper, decide separately:

  1. Failure reporting: should an emitted error reject immediately?
  2. Closure observation: must the caller still know when stdio has closed?

If both matter, keep an independent callback listener for close active after immediate rejection, and maintain an error observer throughout the observation period. Record errors separately and guard shared finalization side effects against duplicate execution. A rejected once(child, 'close') cannot substitute for observing close: rejection may have come from error instead. Lifecycle semantics · once error handling

There is one important exception to this error-rejection behavior: once(child, 'error') fulfills with the error event’s arguments. Special error rejection applies when waiting for another event. Waiting for error itself

Register every required wait before awaiting

Install observers and create the required Promise waits immediately after spawn() returns, without an intervening await. Do not make the close subscription depend on finishing an exit await.

The Events documentation explains the registration hazard: conventional event listeners run synchronously, while Promise continuations run after the current execution stack completes. Multiple events can therefore occur before execution resumes after an await. It also identifies process.nextTick() ordering as a case where this matters. Its recommendation is to create all required Promises before awaiting any of them. This is a general event-registration hazard, not a claim that every ChildProcess run reproduces it. Multiple-event waiting caveat

When creating several waits, attach rejection handling to all of them immediately. For diagnostics, record event order inside the event callbacks, separately from when Promise handlers run.

Unexecuted test procedure for your runtime

This procedure has not been executed. It reports no observed outcomes or validated remedy. Use it to examine the selected contracts on your actual Node.js version and OS.

  1. Record the environment and set a bound. Note the exact Node.js version, OS, and absolute path to the Node.js executable. Consult matching version-pinned documentation. Choose an observation deadline and an OS-appropriate cleanup plan before running each case. If inspecting the three proposed method names at runtime, record their presence separately from any investigation of their behavior.

  2. Prepare isolated cases. In a temporary directory, prepare a child fixture intended to write a short stdout marker and finish with status zero; another intended to write a marker and finish with status seven; and a long-running fixture that writes a readiness marker and remains active. For spawn failure, choose an absolute executable path in that directory and verify that no file exists there. Spawn directly, without a shell.

  3. Install instrumentation immediately. Arm the deadline before spawning. As soon as spawn() returns a child object, synchronously register callbacks for spawn, error, exit, and close. Record event names and arguments inside those callbacks. Begin consuming stdout and stderr when present. Keep the error observer and an independent close observer active even if a Promise rejects. Record any exception from the spawn call separately, rather than inventing an event entry for it. ChildProcess events and streams

  4. Create and handle all waits before yielding. Create once(child, 'exit') and once(child, 'close'). Derive a third Promise from the close wait using the table’s zero-code/no-signal policy. Immediately attach fulfillment and rejection recording handlers to each wait and use Promise.allSettled() to collect settlements. Keep the deadline independent of that aggregate wait. Record Promise settlements separately from the callback event log. Promise registration guidance

  5. Run each case separately. Run the zero-status, nonzero-status, and nonexistent-executable cases. For signal termination, request an OS-supported termination signal only after capturing the long-running fixture’s complete readiness marker. Record the request’s result, any thrown or emitted error, and the actual termination arguments. If unsupported, record a skip and its reason. Do not equate sending a signal with observing termination. Signal-delivery caveats

  6. Observe closure and clean up. Continue independent closure observation even if all Promise waits have already rejected. If the deadline expires, record pending waits and the case as incomplete, then carry out cleanup. If using a separate AbortSignal with events.once() to cancel pending waits, label those settlements as wait cancellations—not observations of process closure. Keep lifecycle monitoring active during cleanup, then remove the test observers. Wait cancellation

Populate this empty template only from actual runs:

Node.js version OS case event sequence code/signal Promise settlement notes

Keep the implementation decision explicit: which event ends the wait, which status counts as success, and whether failure reporting must remain separate from closure observation. Verify convenience methods against your release rather than inferring availability or behavior from their proposed names.

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗