Skip to content

Command Validators are still run when a parser fails #2645

@Choosechee

Description

@Choosechee

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:

using System.CommandLine;

Option<int> intOption = new("--int")
{
    Validators =
    {
        result =>
        {
            int value = result.GetValueOrDefault<int>();
            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.<<Main>$>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.<Main>$(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:

using System.CommandLine;

Option<int> 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<int>();
            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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions