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:
closefollowsexit, or followserrorif the child failed to spawn. - Errors: an
errorevent does not guarantee a subsequentexitevent. 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:
- Failure reporting: should an emitted error reject immediately?
- 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.
-
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.
-
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.
-
Install instrumentation immediately. Arm the deadline before spawning. As soon as
spawn()returns a child object, synchronously register callbacks forspawn,error,exit, andclose. 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 -
Create and handle all waits before yielding. Create
once(child, 'exit')andonce(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 usePromise.allSettled()to collect settlements. Keep the deadline independent of that aggregate wait. Record Promise settlements separately from the callback event log. Promise registration guidance -
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
-
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
AbortSignalwithevents.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 are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.
Read our editorial approach ↗