fix(cli): set non-zero exit code on argument errors#60923
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c820715598
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
c820715 to
f0e3fd7
Compare
Greptile SummaryThis PR fixes a long-standing CLI bug where The implementation correctly addresses both concerns raised in the prior review round:
Key observations:
Confidence Score: 5/5Safe to merge — the logic fix is correct, the prior P1 rethrow concern is resolved, and no new correctness issues were found. All remaining findings are P2 style/test-isolation concerns. The core behavior (setting exit code, aborting parse before running the action handler) is verified by the new unit tests. No P0 or P1 issues remain. src/cli/run-main.exit.test.ts — missing mocks for enableConsoleCapture and installUnhandledRejectionHandler could cause test contamination across the suite. Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/cli/run-main.exit.test.ts
Line: 170-186
Comment:
**Missing mocks for side-effectful imports in integration test**
The `"swallows Commander parse exits"` test exercises the full `runCli` code path through the Commander `parseAsync` call, which means two un-mocked modules are invoked with their real implementations:
1. **`enableConsoleCapture`** (static import from `../logging.js`, called at `run-main.ts:175`) — patches global `console.*` methods and registers EPIPE listeners on `process.stdout`/`process.stderr`. These patches persist for the lifetime of the test process.
2. **`installUnhandledRejectionHandler`** (dynamically imported from `../infra/unhandled-rejections.js`, called at `run-main.ts:183`) — attaches a `process.unhandledRejection` listener that can invoke `process.exit(1)` for fatal rejections. Because the listener is never removed, it remains active in all subsequent tests in the same process.
Neither module appears in the `vi.mock()` list for this file. Mocking both would prevent global console patching and stop the persistent `unhandledRejection` listener from triggering unexpected exits in other tests.
```ts
vi.mock("../logging.js", () => ({ enableConsoleCapture: vi.fn() }));
vi.mock("../infra/unhandled-rejections.js", () => ({
installUnhandledRejectionHandler: vi.fn(),
}));
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (3): Last reviewed commit: "cli: silence commander tests and rethrow..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0e3fd7057
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1659b04c28
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
3e99fe1 to
8c10f0e
Compare
|
@greptileai review |
There was a problem hiding this comment.
Pull request overview
This PR fixes a CLI regression where Commander.js printed argument/usage errors but the process still exited with code 0, which breaks scripting/monitoring that relies on non-zero exit codes for failures.
Changes:
- Add a
program.exitOverridehandler to ensureprocess.exitCodeis set based on Commander’s computederr.exitCode(defaulting to1). - Add unit tests covering argument errors, help/version exits, and help error paths (including
{ from: "user" }parsing). - Document the behavior change in
CHANGELOG.md.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/cli/program/build-program.ts | Sets process.exitCode during Commander exit flows to preserve correct non-zero codes for argument/help-error paths. |
| src/cli/program/build-program.test.ts | Adds regression tests validating exit code behavior when process.exit is intercepted. |
| CHANGELOG.md | Records the CLI/Commander exit-code fix and the added regression coverage. |
8ba7616 to
793ed27
Compare
|
@greptileai review |
9f23926 to
a783109
Compare
Fixes openclaw#60905 When CLI commands receive invalid arguments (e.g., `openclaw sessions list`), Commander.js prints an error but was returning exit code 0. This broke scripts and monitoring tools that rely on exit codes to detect failures. Solution: Call `program.exitOverride()` with a callback that sets `process.exitCode = 1` for all errors except help/version displays. Before: ```bash $ openclaw sessions list 2>&1 error: too many arguments... $ echo $? 0 ``` After: ```bash $ openclaw sessions list 2>&1 error: too many arguments... $ echo $? 1 ```
Add unit tests to verify: - Argument errors set exitCode = 1 - --help sets exitCode = 0 (commander.helpDisplayed) - --version sets exitCode = 0 (commander.version) Also fix exitOverride to handle commander.helpDisplayed in addition to commander.help for proper help display handling.
a783109 to
0de0c43
Compare
|
Merged via squash.
Thanks @Linux2010! |
Summary
Fixes #60905
When CLI commands receive invalid arguments (e.g.,
openclaw sessions list), Commander.js prints an error but was returning exit code 0. This broke scripts and monitoring tools that rely on exit codes to detect failures.Solution
Call
program.exitOverride()with a callback that setsprocess.exitCode = 1for all errors except help/version displays.Before
After
Test Coverage
Added 3 new unit tests to verify exit code behavior:
exitCode = 1--helpsetsexitCode = 0(handlescommander.helpDisplayed)--versionsetsexitCode = 0Test Plan
pnpm test src/cli/program/build-program.test.ts--help) still exits with code 0--version) still exits with code 0