diff --git a/CliFx.Tests/ErrorReportingSpecs.Commands.cs b/CliFx.Tests/ErrorReportingSpecs.Commands.cs
index 455dba7..28475b7 100644
--- a/CliFx.Tests/ErrorReportingSpecs.Commands.cs
+++ b/CliFx.Tests/ErrorReportingSpecs.Commands.cs
@@ -20,7 +20,7 @@ namespace CliFx.Tests
private class CommandExceptionCommand : ICommand
{
[CommandOption("code", 'c')]
- public int ExitCode { get; set; } = 1337;
+ public int ExitCode { get; set; } = 133;
[CommandOption("msg", 'm')]
public string? Message { get; set; }
diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs
index d8c84b7..1e46cfd 100644
--- a/CliFx/CliApplication.cs
+++ b/CliFx/CliApplication.cs
@@ -263,7 +263,7 @@ namespace CliFx
public static int FromException(Exception ex) =>
ex is CommandException cmdEx
? cmdEx.ExitCode
- : ex.HResult;
+ : 1;
}
[Command]
diff --git a/CliFx/Exceptions/CommandException.cs b/CliFx/Exceptions/CommandException.cs
index 844f2b7..a748888 100644
--- a/CliFx/Exceptions/CommandException.cs
+++ b/CliFx/Exceptions/CommandException.cs
@@ -9,12 +9,14 @@ namespace CliFx.Exceptions
///
public class CommandException : Exception
{
- private const int DefaultExitCode = -1;
+ private const int DefaultExitCode = 1;
private readonly bool _isMessageSet;
///
/// Returns an exit code associated with this exception.
+ /// On Unix systems an exit code is 8-bit unsigned integer so it's strongly recommended to use exit codes between 1 and 255
+ /// otherwise it may overflow and yield unexpected results.
///
public int ExitCode { get; }
@@ -26,6 +28,14 @@ namespace CliFx.Exceptions
///
/// Initializes an instance of .
///
+ /// The exception message.
+ /// The inner exception.
+ ///
+ /// The exit code associated with this exception.
+ /// On Unix systems an exit code is 8-bit unsigned integer so it's strongly recommended to use exit codes between 1 and 255
+ /// otherwise it may overflow and yield unexpected results.
+ ///
+ /// Whether to show the help text after handling this exception.
public CommandException(string? message, Exception? innerException, int exitCode = DefaultExitCode, bool showHelp = false)
: base(message, innerException)
{
@@ -39,6 +49,13 @@ namespace CliFx.Exceptions
///
/// Initializes an instance of .
///
+ /// The exception message.
+ ///
+ /// The exit code associated with this exception.
+ /// On Unix systems an exit code is 8-bit unsigned integer so it's strongly recommended to use exit codes between 1 and 255
+ /// otherwise it may overflow and yield unexpected results.
+ ///
+ /// Whether to show the help text after handling this exception.
public CommandException(string? message, int exitCode = DefaultExitCode, bool showHelp = false)
: this(message, null, exitCode, showHelp)
{
@@ -47,6 +64,12 @@ namespace CliFx.Exceptions
///
/// Initializes an instance of .
///
+ ///
+ /// The exit code associated with this exception.
+ /// On Unix systems an exit code is 8-bit unsigned integer so it's strongly recommended to use exit codes between 1 and 255
+ /// otherwise it may overflow and yield unexpected results.
+ ///
+ /// Whether to show the help text after handling this exception.
public CommandException(int exitCode = DefaultExitCode, bool showHelp = false)
: this(null, exitCode, showHelp)
{
diff --git a/Readme.md b/Readme.md
index edd70ed..0bbeb55 100644
--- a/Readme.md
+++ b/Readme.md
@@ -396,10 +396,12 @@ You can run `myapp.exe cmd1 [command] --help` to show help on a specific command
You may have noticed that commands in CliFx don't return exit codes. This is by design as exit codes are considered a higher-level concern and thus handled by `CliApplication`, not by individual commands.
-Commands can report execution failure simply by throwing exceptions just like any other C# code. When an exception is thrown, `CliApplication` will catch it, print the error, and return an appropriate exit code to the calling process.
+Commands can report execution failure simply by throwing exceptions just like any other C# code. When an exception is thrown, `CliApplication` will catch it, print the error, and return an exit code `1` to the calling process.
If you want to communicate a specific error through exit code, you can instead throw an instance of `CommandException` which takes an exit code as a parameter. When a command throws an exception of type `CommandException`, it is assumed that this was a result of a handled error and, as such, only the exception message will be printed to the error stream. If a command throws an exception of any other type, the full stack trace will be printed as well.
+> Please note that on Unix systems an exit code is 8-bit unsigned integer so it's strongly recommended to use exit codes between `1` and `255` otherwise it may overflow and yield unexpected results.
+
```c#
[Command]
public class DivideCommand : ICommand
@@ -414,8 +416,8 @@ public class DivideCommand : ICommand
{
if (Math.Abs(Divisor) < double.Epsilon)
{
- // This will print the error and set exit code to 1337
- throw new CommandException("Division by zero is not supported.", 1337);
+ // This will print the error and set exit code to 133
+ throw new CommandException("Division by zero is not supported.", 133);
}
var result = Dividend / Divisor;
@@ -434,7 +436,7 @@ Division by zero is not supported.
> $LastExitCode
-1337
+133
```
You can also specify the `showHelp` parameter to instruct whether to show the help text for the current command after printing the error: