Robust child process management in NodeJS? Not so easy :-).
For example, calling child_process.spawn()
with a bad path does not throw an error right away. Although that error (ENOENT in this case, on Linux) is indeed known to the runtime in a synchronous fashion (NodeJS uses uv_spawn() for invoking the new process and that reports errors synchronously to its caller).
Even when child_process.spawn()
fails to create a process it will return a process handle. Which you can try to kill(). And that would not throw an error right away.
To discuss this topic I submitted https://github.com/nodejs/node/issues/30668 a while back (as of which we improved the documentation for kill()) and https://github.com/nodejs/node/issues/33566.
Some more aspects and links, in no particular order:
- A discussion like https://github.com/nodejs/node/pull/21234 shows that it’s of course expensive to do the heavy lifting required for spawning a new process as part of the event loop — what’s the upside? I think the major upside here is indeed that error information indeed is available synchronously! But why not use it, then, immediately?
- I would expect the documentation to help programmers with how to reliably handle common errors such as ENOENT and EACCES happening during using spawn().
- https://github.com/eclipse-theia/theia/pull/3447
- https://github.com/nodejs/node/issues/14917
- Lovely introduction to the challenge: https://stackoverflow.com/questions/49577641/synchronously-checking-if-a-child-process-failed-to-spawn-in-node
- https://github.com/nodejs/help/issues/1191
- On the Interwebz, the question whether or not the child process actually spawned successfully is often addressed with techniques based on proc.pid — this is really error-prone because it’s entirely unclear when and how often this is actually set. How dynamic is this? How much of its behavior is implementation detail, how much of it is stable API?
A successful (but I think a little too involved) recipe for handling startup errors might be this:
- spawn()
- attach error handler, and upon error store the error state in a magic variable.
- implement and start a “success criterion watcher” (may even be based on polling) and in that logic consider the magic variable (for example, if it is known to be an error object, throw it).
Leave a Reply