feat(cli-v2): map CliError.Code to sysexits-style exit codes#16041
Open
iamnamananand996 wants to merge 3 commits into
Open
feat(cli-v2): map CliError.Code to sysexits-style exit codes#16041iamnamananand996 wants to merge 3 commits into
iamnamananand996 wants to merge 3 commits into
Conversation
Adds exitCodeForError() / exitCodeForCliErrorCode() that map every CliError.Code variant to a distinct process exit code following the BSD sysexits.h convention: - USER_ERROR -> 2 (usage) - VALIDATION/PARSE/IR/REFERENCE -> 65 (data err) - RESOLUTION_ERROR -> 66 (no input) - INTERNAL_ERROR -> 70 (software) - AUTH_ERROR -> 77 (no perm) - CONFIG_ERROR / VERSION_ERROR -> 78 (config) - NETWORK / CONTAINER / ENVIRONMENT -> 1 (generic) - SIGINT -> 130 - SIGTERM -> 143 Both the central handleError boundary in withContext.ts and the shared yargs .fail handler in yargsFailHandler.ts go through the same mapping, so shell scripts and CI pipelines can branch on failure type without parsing stderr. assertNever in the switch guarantees a compile error if a new CliError.Code variant is added without picking an exit code.
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
This was referenced May 21, 2026
Update three v2 ete tests that asserted on the old generic exit code 1 to match the new sysexits mapping introduced in this PR: - `fern check` on an invalid fern.yml → 65 (EX_DATAERR for validation failures) - `fern check --api <missing>` → 78 (EX_CONFIG for user-config failures) - `fern sdk generate --target <not-configured>` → 78 (EX_CONFIG for user-config failures) Caught by test-ete on the first CI run for this PR.
Add ExitCode.TempFail (75) for transient failures and remap NETWORK/CONTAINER/ENVIRONMENT errors to it so these cases are treated as retryable. TaskAbortSignal now carries an optional CliError.Code; failAndThrow forwards the code when throwing, and exitCodeForError maps the signal's code (or falls back to Generic if absent). Tests and changelog updated accordingly, and withContext signal handler was simplified to reference ExitCode directly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Linear ticket: Refs FER-10016 — wave-2, recommendation R5.
Stacked on top of #16040 (wave-1). This PR targets the wave-1 branch so reviewers see only the wave-2 delta; once #16040 merges I'll re-target this to
main.Today every cli-v2 failure exits with
1, regardless of whether the user mistyped a flag, hit an auth wall, fed in a malformedfern.config.json, or tripped an internal bug. Shell scripts and CI pipelines have togrepstderr to tell the difference. This PR maps eachCliError.Codeto a distinct process exit code following the BSDsysexits.hconvention (the same onegit,curl, and most well-behaved Unix tools use), so callers can branch on failure type without parsing strings.Both the central
handleErrorboundary inwithContext.tsand the shared yargs.failhandler inyargsFailHandler.tsgo through the same mapping, sofern foo --no-such-flagandthrow new CliError({ code: UserError })both exit with2.Changes Made
packages/cli/cli-v2/src/errors/exitCode.tsexportingExitCode,exitCodeForError(), andexitCodeForCliErrorCode(). The switch usesassertNeverfrom@fern-api/core-utils, so adding a newCliError.Codevariant without picking an exit code will fail compilation.withContext.ts: route the failure-path exit throughexitCodeForError(error)instead of hardcoded1. Success path uses the newExitCode.Successconstant. Signal handlers now referenceExitCode.Sigint/ExitCode.Sigtermrather than literal128 + signalmath.yargsFailHandler.ts: drop the previous "stick with 1 today" workaround and route throughexitCodeForError(error)so the synthesizedUSER_ERRORexits 2.packages/cli/cli-v2/src/__test__/exitCode.test.ts(12 tests) covering everyCliError.Codevariant,TaskAbortSignal, genericError/TypeError, non-Errorthrowables (string/number/undefined/null), and the canonical 128+signal exit codes.Updated README.md generator(n/a)Behavior change & migration
Existing CI scripts that currently do
fern check; if [ $? -eq 1 ]will still work forNETWORK_ERROR/CONTAINER_ERROR/ENVIRONMENT_ERRORand unhandled exceptions (still1), but other failures now exit with a more specific code. Scripts that branched on "exit 1 = anything went wrong" should update toif [ $? -ne 0 ].This is shipping as
feat(minor bump) because it changes shell-visible behavior, even though every value is more correct than before.Testing
packages/cli/cli-v2/src/__test__/exitCode.test.ts(12 tests, allCliError.Codevariants covered).pnpm format,pnpm lint:biome,pnpm checkall green.fern beta auth whoami(no token) → exit 77fern beta auth whoamii(typo, yargs.fail) → exit 2fern beta checkoutside any project → exit 78fern beta whatever(top-level unknown command) → exit 2Link to Devin session: https://app.devin.ai/sessions/c00fe336eaa44387a47db9083451e93c
Requested by: @iamnamananand996