Errors
Input goes through four stages, and something can go wrong in any of them:
- Definition: the CLI and subcommand definitions are checked for problems such as duplicate names or unknown dependencies.
- Parsing: terminal input is read and matched against the definition.
- Validation: schemas run, then the constraint rules are applied.
- Execution: the matching handlers run, once everything else has passed.
The first three produce a CliError. run returns it instead of throwing:
const result = cli.run(process.argv.slice(2));if (result.error) { console.error(result.error.message); process.exit(1);}Stage four is yours. Anything that a handler throws propagates to the caller, and with an async handler you need runAsync for the rejection to be awaited.
execute and executeAsync throw the CliError rather than returning it, since there is no result object to put it in.
Narrowing an error
Every CliError carries three fields:
| Field | What it tells you |
|---|---|
cause | Which stage it came from: Internal, Definition, Parse, Validation. |
code | The specific problem. |
context | The details behind it, typed per code. |
import { ErrorCause, ValidationErrorCode } from "@staticbolt/args-parser";
const result = cli.run(input);
if (result.error) { // By stage if (result.error.cause === ErrorCause.Parse) { // a flag was unknown, a value was missing, and so on }
// By specific code if (result.error.code === ValidationErrorCode.SchemaValidationFailed) { const { commandKind, commandName, kind, name, inputValue, issues } = result.error.context;
console.error(`the ${kind} "${name}" of ${commandKind} "${commandName}" rejected:`, inputValue); console.error(issues); }}Narrowing on code narrows context with it, so you only see the fields that code actually provides.
Every code is listed in Error types.
A usable error message
error.message is already written for the end user. A common pattern is to print it with a hint about help:
if (result.error) { console.error(result.error.message); console.error("\nRun `listy --help` for usage.\n"); process.exit(1);}Definition errors mean your CLI itself is wrong, not the user's input. They show up the first time you run it, so a quick smoke test in CI is enough to catch them.