dotnet / dotnet/command-line-api
Command Validators are still run when a parser fails
- Dominant language
- C#
- Stars
- 3.7k
- Forks
- 428
- PR merge metrics
- No merged PRs in 30d
Description
If the default or custom parser for an `Option` adds an error, its `Validators` are still run. This is a problem because an exception will be thrown when a validator tries to get the value. Here is an example:
```cs
using System.CommandLine;
Option intOption = new("--int")
{
Validators =
{
result =>
{
int value = result.GetValueOrDefault();
if (value < 1)
result.AddError("Value must be at least 1.");
}
}
};
RootCommand command = new()
{
intOption
};
command.SetAction(result => Console.WriteLine("Command ran successfully"));
command.Parse(["--int", "nonsense"]).Invoke();
```
Running the code on my system gives this hostile error message:
```
Unhandled exception. System.InvalidOperationException: Cannot parse argument 'nonsense' for option '--int' as expected type 'System.Int32'.
at System.CommandLine.Binding.ArgumentConverter.GetValueOrDefault[T](ArgumentConversionResult result)
at System.CommandLine.Parsing.OptionResult.GetValueOrDefault[T]()
at Program.<>c.<$>b__0_1(OptionResult result) in /home/choosechee/RiderProjects/CommandLineIssue/CommandLineIssue/Program.cs:line 9
at System.CommandLine.Parsing.CommandResult.ValidateOptions(Boolean completeValidation)
at System.CommandLine.Parsing.CommandResult.Validate(Boolean completeValidation)
at System.CommandLine.Parsing.ParseOperation.Validate()
at System.CommandLine.Parsing.ParseOperation.Parse()
at System.CommandLine.Parsing.CommandLineParser.Parse(Command command, IReadOnlyList`1 arguments, String rawInput, CommandLineConfiguration configuration)
at System.CommandLine.Parsing.CommandLineParser.Parse(Command command, IReadOnlyList`1 args, CommandLineConfiguration configuration)
at System.CommandLine.Command.Parse(IReadOnlyList`1 args, CommandLineConfiguration configuration)
at Program.$(String[] args) in /home/choosechee/RiderProjects/CommandLineIssue/CommandLineIssue/Program.cs:line 22
Process finished with exit code 134.
```
If I remove the validator, it gives the expected friendlier error message:
```
Cannot parse argument 'nonsense' for option '--int' as expected type 'System.Int32'.
Description:
Usage:
CommandLineIssue [options]
Options:
-?, -h, --help Show help and usage information
--version Show version information
--int
Process finished with exit code 0.
```
It happens with a `CustomParser` as well:
```cs
using System.CommandLine;
Option intOption = new("--int")
{
CustomParser = result =>
{
if (int.TryParse(result.Tokens.Single().Value, out int value))
return value;
result.AddError("Value must be an integer."); // cause of exception
return -1;
},
Validators =
{
result =>
{
int value = result.GetValueOrDefault();
if (value < 1)
result.AddError("Value must be at least 1.");
}
}
};
RootCommand command = new()
{
intOption
};
command.SetAction(result => Console.WriteLine("Command ran successfully"));
command.Parse(["--int", "nonsense"]).Invoke();
```
```
Unhandled exception. System.InvalidOperationException: Value must be an integer.
at System.CommandLine.Binding.ArgumentConverter.GetValueOrDefault[T](ArgumentConversionResult result)
at System.CommandLine.Parsing.OptionResult.GetValueOrDefault[T]()
...yadda yadda yadda
Process finished with exit code 134.
```
I can put the validator code that gets the value in a try-catch block to fix this, but I think this is unintuitive to have to do this. I think `Validators` should not be run when parsing fails, or at least run in a try-catch block to handle these exceptions.
Contributor guide
Assessment
This issue has not been assessed yet.