Skip to content

fix: use the error's oclif exit code when --json is enabled - #1654

Open
mikaelkaron wants to merge 1 commit into
oclif:mainfrom
mikaelkaron:fix/json-exit-code
Open

mikaelkaron wants to merge 1 commit into
oclif:mainfrom
mikaelkaron:fix/json-exit-code

Conversation

@mikaelkaron

Copy link
Copy Markdown

What does this PR do?

With --json enabled, Command.catch sets the exit code from err.exitCode:

process.exitCode = process.exitCode ?? err.exitCode ?? 1

CLIError and ExitError don't set exitCode; they carry their code in err.oclif.exit. So under --json, every failure exits with 1:

this.error('Missing argument', {exit: 2}) // exit 2, but exit 1 with --json
this.exit(3)                              // exit 3, but exit 1 with --json

Without --json, handle() already exits with err.oclif.exit. A script that checks $? gets a different code depending on whether it asked for JSON.

This PR makes catch prefer err.oclif.exit, then err.exitCode, then 1:

process.exitCode = process.exitCode ?? err.oclif?.exit ?? err.exitCode ?? 1

CommandError gains the optional oclif metadata (Partial<OclifError>), so the type matches what catch receives.

Testing

New tests in test/command/command.test.ts, under --json exit code:

  • this.error(..., {exit: 2}) exits 2;
  • this.exit(3) exits 3;
  • an error with exitCode: 4 and no oclif.exit still exits 4;
  • a plain Error still exits 1.

The first two fail on main (exit 1) and pass with this change. yarn test passes (836), and so do yarn lint and test:circular-deps.

@salesforce-cla

salesforce-cla Bot commented Oct 1, 2026

Copy link
Copy Markdown

Thanks for the contribution! Unfortunately we can't verify the commit author(s): Mikael Karon <e***@g***.com>. One possible solution is to add that email to your GitHub account. Alternatively you can change your commits to another email and force push the change. After getting your commits associated with your GitHub account, sign the Salesforce Inc. Contributor License Agreement and this Pull Request will be revalidated.

With --json, Command.catch sets process.exitCode from err.exitCode, which CLIError and
ExitError don't set; they carry their code in err.oclif.exit. So under --json every failure
exits 1: this.error('...', {exit: 2}) and this.exit(3) included. Without --json, handle()
already exits with err.oclif.exit.

catch now prefers err.oclif.exit, then err.exitCode, then 1, so a command exits with the same
code with or without --json. CommandError gains the optional oclif metadata.
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.

1 participant