Skip to content

fix: print CLI errors once, without a stack trace, and honor exitCode - #18

Merged
facundofarias merged 1 commit into
mainfrom
fix/print-cli-errors-once
Oct 28, 2026
Merged

facundofarias merged 1 commit into
mainfrom
fix/print-cli-errors-once

Conversation

@facundofarias

Copy link
Copy Markdown
Member

Problem

Every CliError printed twice with a stack trace, and the exit code was always 1:

 ERROR  Not authenticated. Run pixelvault register ...
    at m (dist/index.js:...)
    at runCommand (node_modules/citty/...)
 ...
 ERROR  Not authenticated. Run pixelvault register ...

Cause: citty's runMain handles errors itself. For any non-citty error it logs the error with its stack, logs the message again, and calls process.exit(1). So the .catch after runMain in index.ts never ran, and CliError.exitCode was ignored.

Fix

New src/lib/run.ts (runCli), used by index.ts:

  • --help / --version → still runMain (it renders them well).
  • Everything else → runCommand, with our own error handling:
    • CliError → one Error: … line on stderr, exit with its exitCode.
    • citty usage errors (missing argument, unknown command) → replayed through runMain for its usage rendering. These are thrown while parsing, before any command body runs, so nothing with side effects is re-executed (covered by a test).
    • Any other error → one line, exit 1.

Test plan

  • typecheck, build, 62 tests (new tests/lib/run.test.ts: single line / no stack / exitCode / no re-run / success / usage error)
  • Built binary: pixelvault usage with no key prints one line and exits 1; --version, --help unchanged

🤖 Generated with Claude Code

citty's runMain swallows every error itself: for a non-citty error it prints the
error with a stack trace, prints the message again, and always exits 1. So every
CliError printed twice with a stack, its exitCode was ignored, and the .catch
after runMain never ran.

runCli hands --help/--version to runMain and runs everything else through
runCommand: CliError -> one "Error: ..." line on stderr + its exitCode;
citty usage errors (missing arg, unknown command) are replayed through runMain
for its usage rendering (they occur before any command body runs, so no side
effects are replayed); unexpected errors -> one line, exit 1.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@facundofarias
facundofarias merged commit 4ad87a2 into main Oct 28, 2026
2 checks passed
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.

1 participant