Skip to content

Reduce exit codes to invocation, transport and assertion - #86

Merged
korya merged 1 commit into
masterfrom
korya-feat-exit-codes
Aug 9, 2026
Merged

korya merged 1 commit into
masterfrom
korya-feat-exit-codes

Conversation

@korya

@korya korya commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Problem

A CI job reading the exit code could not tell an unreachable service from one that answered wrongly, nor a forgotten flag from a network failure. The five codes had accidental boundaries — assigned by whichever internal layer failed, not by what kind of failure it was: the same typo exited 103 (with a 40-line usage dump) from the command line but 71 (one clean line) from the environment; a run with zero assertions reported the transport-failure code (#25); and the distinction the docs promise — "a broken service vs a broken invocation" — was exactly the one the codes didn't make.

Solution

Three non-zero codes, each answering "whose fault?": 71 — the invocation was rejected, no request attempted; 92 — no usable response arrived; 93 — a response arrived and an assertion failed.

Before → After:

Condition Before After
bad flag value / combination 71 71
bad value via environment 71 71
type typo, unknown flag, arg count 103 + usage dump 71, one line
unbuildable request (-X, URL) 91 71
zero assertions (#25) 93 71
unreachable / TLS / timeout / redirect bound 93 92
assertion failed 93 93

Failure category is decided where it's understood — inside the attempt, where transport and assertion failures part ways — and carried to the exit in a typed error that survives the retry wrapper via %w/errors.As, so retry exhaustion inherits the last attempt's category. An untagged error defaults to the transport bucket: wrong by at most one category, never misreported as the caller's mistake. Cobra's own error printing is silenced so every failure exits through the same one-line path.

This is a deliberate breaking change, pre-1.0: 91/103 are gone and transport failures leave 93. Scripts testing non-zero are unaffected; the README carries a migration note.

Other Changes

  • TestE2EExitCodes now covers all three categories plus both invocation channels — including the CLI-vs-environment typo pair that used to exit differently, and the zero-assertion case flipped out of the known-issues file per its protocol (closes Zero assertions is reported as "Cannot perform request" with exit 93 #25).
  • The help-sync test enumerates the new set and asserts the retired codes (2, 91, 103) are no longer advertised.
  • Every exit-code assertion in the e2e suites was re-triaged individually (transport vs assertion), not batch-renamed.

Related:

🤖 Generated with Claude Code

https://claude.ai/code/session_01TiEcuZhTyL5XbrCwyiHau8

The exit code now answers whose fault the failure is: 71 means the
invocation was rejected and no request was attempted, 92 means the request
produced no usable response, and 93 means a response arrived and at least
one assertion failed.

Previously the code was a property of whichever layer happened to fail,
which produced five codes with accidental boundaries: the same typo exited
103 with a 40-line usage dump from the command line but 71 with one line
from the environment; a redirect bound and a failed assertion shared 93;
and running with zero assertions -- detected before any request -- reported
the transport-failure code (#25).

Failures are now tagged with their category where they are understood, in
doOnce, and carried to the exit in a typed error that survives the retry
wrapper. Retry exhaustion reports the category of the last attempt. An
untagged error falls into the transport bucket: wrong by at most one
category, never reported as a usage mistake. Cobra's own error reporting is
silenced in favour of the same one-line dief path every other error takes.

BREAKING CHANGE: exit codes 91 and 103 are gone (both now 71), and
transport failures moved from 93 to 92; 93 now always means an assertion
failed. Scripts that only test for non-zero are unaffected.

Closes #25

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiEcuZhTyL5XbrCwyiHau8
@korya
korya force-pushed the korya-feat-exit-codes branch from 3b588ac to ccbc891 Compare August 9, 2026 03:19
@korya
korya marked this pull request as ready for review August 9, 2026 03:22
@korya
korya merged commit 2f4b3e1 into master Aug 9, 2026
8 checks passed
@korya
korya deleted the korya-feat-exit-codes branch August 9, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zero assertions is reported as "Cannot perform request" with exit 93

1 participant