NAME

App::karr::Role::ExitCodes - Normalize MooX::Options option-parse errors to exit code 2 (ADR 0002)

VERSION

version 0.600

DESCRIPTION

Part of karr's exit-code contract (ADR 0002): 0 success, 1 runtime failure, 2 usage error.

MooX::Options handles an option-parse failure -- an unknown option, an invalid option value, or a missing required option -- by printing a diagnostic and then calling $class->options_usage($code) with a positive $code, which exits that code. Historically that code was 1, which collided with genuine runtime failures. Those are usage errors, so this role wraps options_usage to force any positive (error) code to 2.

Help requests (-h, --help, --usage) reach options_usage with a code of 0 (or undef), so they are left untouched: they still print to STDOUT and exit 0.

The role also decides where that diagnostic is printed. MooX::Options puts it in front of a fifteen-line usage block; karr puts it behind it, together with the invocation that would have worked -- see "THE ANSWER GOES LAST" below.

The complementary half of the contract -- catching the uncaught dies raised by command bodies and classifying them into runtime (1) versus usage (2) -- lives in the central handler in bin/karr. That handler classifies by a stable leading marker on the message, so a command that wants to reject an invalid invocation itself calls usage_error, which emits the generic marker. The root command's own option-parse errors go through its _print_help override instead of this role, and that override applies the same positive-to-2 remap and calls the same reordering method.

THE ANSWER GOES LAST

An option-parse error used to read like this, and the first line was the answer:

Option claim requires an argument
USAGE: karr move ID[,ID,...] STATUS [--claim NAME] [--next|--prev]
    ... thirteen more lines ...
    --man           show the manual

karr's primary callers are agents, which read it through tail -n because the output is long -- so the line that says what went wrong is exactly the line that gets cut, and the four-call reproduction in ticket k263 is what that costs. The same error now ends where it is read:

USAGE: karr move ID[,ID,...] STATUS [--claim NAME] [--next|--prev]
    ... the same thirteen lines ...
    --man           show the manual
Option claim requires an argument:
  karr move 1 in-progress --claim NAME

The diagnostic is MooX::Options' own, word for word; only its position changes, and the colon and the line under it are added. That line is "command_hint" in App::karr::Error over the argv the caller really typed ("original_argv" in App::karr::Error), so it shows --claimed-by where that is what was typed rather than the --claimed_by Getopt::Long is handed, and the placeholder is the word the command's own usage_string uses for that value.

A suggestion is offered only where karr has an honest one. Unknown option: has none -- the line that would run is the caller's own word deleted -- so it gets the reordering, no suggestion, and therefore no colon either. So does a diagnostic whose shape is not recognised, and so does any of them where no argv was recorded: a library caller, a test, bin/karr-foundation.

usage_error

$self->usage_error("--last must be 1 or greater (got 0)");

Dies with a message carrying the Usage error: marker that bin/karr classifies as a usage error, so the process exits 2.

This is for misuse that MooX::Options cannot catch on its own: an option value that parses but is out of range, a mutually exclusive combination of flags, an argument list that is syntactically fine but semantically empty. Anything MooX::Options can catch -- an unknown option, a value that does not fit the declared format -- already exits 2 through the options_usage wrapper above and must not be re-checked here.

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/karr/issues.

IRC

Join #langertha on irc.perl.org or message Getty directly.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <getty@cpan.org>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)