Skip to content

fix(cli): propagate runCLI() startup errors instead of swallowing them via process.exit(1)#3774

Open
amitraj2203 wants to merge 2 commits into
WordPress:trunkfrom
amitraj2203:fix/runcli-propagate-startup-errors
Open

fix(cli): propagate runCLI() startup errors instead of swallowing them via process.exit(1)#3774
amitraj2203 wants to merge 2 commits into
WordPress:trunkfrom
amitraj2203:fix/runcli-propagate-startup-errors

Conversation

@amitraj2203

@amitraj2203 amitraj2203 commented Jun 9, 2026

Copy link
Copy Markdown

Motivation for the change, related issues

runCLI() in packages/playground/cli/src/run-cli.ts had a .catch() on the internal startServer() call that swallowed startup errors by printing them and calling process.exit(1):

}).catch((error) => {
    cliOutput.printError(describeError(error));
    process.exit(1);
});

This means library callers of runCLI() — such as WordPress Studio, which spawns it as a daemon — can never catch or handle startup errors. A non-fatal error (e.g. a port conflict, a transient worker exit race) kills the entire process with no opportunity to retry or report it gracefully.

Fixes #3520

Implementation details

Remove the .catch() so rejections from startServer() propagate naturally through await as a rejected promise from runCLI().

The process.exit(1) behavior for standalone CLI users is preserved unchanged: parseOptionsAndRunCLI() already wraps runCLI() in its own try/catch that formats the error and exits.

The existing test should error when explicit port is already in use was asserting the old broken behavior (error printed to stdout + process.exit called). Updated it to assert the correct behavior: runCLI() rejects with EADDRINUSE and process.exit is not called.

Breaking change: library callers that previously relied on runCLI() never rejecting will now see unhandled rejections on startup failure. This is the correct behavior — callers should handle errors from runCLI().

Testing Instructions (or ideally a Blueprint)

  1. Run the CLI test suite and confirm all tests pass:

    nvm use
    npx nx test-playground-cli playground-cli --testFile=run-cli.spec.ts
  2. Confirm the standalone CLI still exits cleanly on a port conflict:

    # Occupy port 9400
    node -e "require('net').createServer().listen(9400)"
    # In another terminal, try to start the CLI on the same port
    npx nx dev playground-cli server --port=9400
    # Should print the EADDRINUSE error and exit with code 1 — same as before
  3. Confirm library callers can now catch errors:

    import { runCLI } from '@wp-playground/cli';
    
    try {
        await runCLI({ command: 'server', port: 9400 }); // port already in use
    } catch (error) {
        console.log('Caught:', error.message); // now reachable
    }

@amitraj2203 amitraj2203 requested review from a team, bgrgicak and Copilot June 9, 2026 14:11
@github-actions github-actions Bot added [Package][@wp-playground] CLI [Type] Bug An existing feature does not function as intended [Aspect] Node.js and removed [Type] Bug An existing feature does not function as intended [Package][@wp-playground] CLI labels Jun 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR changes runCLI() startup failure behavior so errors from startServer() propagate to library callers (instead of being swallowed via process.exit(1)), while preserving standalone CLI exit behavior via the outer wrapper.

Changes:

  • Removed the internal .catch() in runCLI() so startServer() rejections reject runCLI().
  • Updated the “port in use” test to assert runCLI() rejects with EADDRINUSE and that process.exit is not called.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/playground/cli/src/run-cli.ts Stops swallowing startup errors so callers can handle failures instead of an unconditional process exit.
packages/playground/cli/tests/run-cli.spec.ts Adjusts expectations for port-conflict behavior to match the new error propagation semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2137 to 2139
exitSpy.mockRestore();
blockingServer.close();
}
Comment on lines +2128 to +2133
await expect(
runCLI({
command: 'server',
port,
})
).rejects.toThrow(/EADDRINUSE/);
Comment on lines 1830 to 1832
return response;
},
}).catch((error) => {
cliOutput.printError(describeError(error));
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

runCLI() swallows errors via process.exit(1) instead of re-throwing

2 participants