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)