Replace raw stdlib parse errors in typed flag Set methods (#269)#469
Merged
Conversation
Each typed flag's Set method previously returned its strconv (or
time.ParseDuration) error verbatim, which leaks implementation detail
into user-facing CLI errors:
invalid argument "x" for "--bool" flag: strconv.ParseBool: parsing "x": invalid syntax
Replace with human-readable hints, keyed off the flag's underlying type:
invalid argument "x" for "--bool" flag: must be true or false
Covered: bool, int/int8/16/32/64, uint/uint8/16/32/64, float32/64,
duration. Slices and map types fall through to their existing behavior.
Updates the affected line in TestParse, and adds TestInvalidArgumentMessages
to lock in the new messages across types.
Refs spf13#269.
tomasaschan
approved these changes
May 16, 2026
Collaborator
tomasaschan
left a comment
There was a problem hiding this comment.
Thanks!
I'm on the fence on whether it's worth introducing stable errors for the different cases (at least a few strings passed to errors.New are repeated here), and then also whether to export them. The stdlib flag package seems to introduce them unexported, which from a user's perspective isn't that different from the repeated approach here (you can't use Is on them anyway), so I think we can merge this as is and then iterate if anyone asks for stable errors to reference.
CI surfaced two issues in the test changes: - flag_test.go:669 column alignment off after the cases struct rename (gofmt -w produces a 3-line diff) - flag_test.go:688 uses io.Discard which was introduced in Go 1.16; pflag's CI matrix still tests Go 1.12, so the Test (1.12) job failed with 'undefined: io.Discard'. ioutil.Discard works in all Go versions back to 1.0 and is still functional despite the post-1.16 deprecation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #269.
Problem
Each typed flag's
Setmethod returns the strconv (ortime.ParseDuration) error verbatim, so users see internal-looking text:The leading
invalid argument "x" for "--some-flag" flag:shape is fine — it tells the user what failed and where. The trailingstrconv.ParseBool: parsing "x": invalid syntaxis implementation detail leaking to end users.Same shape applies to
int*(strconv.ParseInt),uint*(strconv.ParseUint),float*(strconv.ParseFloat), andduration(time.ParseDuration).Change
Each typed flag's
Setmethod now returns a human-readable hint when the underlying parse fails:boolmust be true or falseint,int8/16/32/64must be an integeruint,uint8/16/32/64must be a non-negative integerfloat32/64must be a numberdurationmust be a duration like "30s" or "5m"Result:
The outer
invalid argument %q for %q flag: %vshape fromInvalidValueError.Error()is unchanged; only the cause changes.Scope
Setmethod each, identical pattern.flag_test.goupdated: the existing assertion intestParse(line 398, the--bool=abcdefgcase) now asserts the new message.TestInvalidArgumentMessagestable-driven test locks in the suffix per type so future drift is caught.Backwards compatibility
The error type returned from
Parseis still*InvalidValueError. Code usingerrors.As(err, &ive)continues to work. Thecausechain (errors.Unwrapto reach the*strconv.NumError) is no longer present, since the new errors are plainerrors.New. Any caller that drilled into the wrapped strconv error would need to switch to inspectingInvalidValueError.GetFlag().Value.Type()instead. I expect such callers to be vanishingly rare; happy to add anerrors.Issentinel if maintainers want a stable handle.