Repository navigation
Open
Description
Problem
The current FieldMask.IsValid(m proto.Message) bool method only returns a boolean indicating whether all paths are valid, but provides no information about which specific paths are invalid. This makes it difficult for developers to:
- Debug invalid field masks - developers have to manually inspect each path to determine which ones are causing validation failures
- Provide meaningful error messages - applications cannot give users specific feedback about which fields are problematic
- Implement incremental fixes - when working with complex field masks, it's hard to identify and fix invalid paths one by one
Proposed Solution
Add a new method GetInvalidPaths(m proto.Message) []string to the FieldMask type that returns a list of invalid paths.
// GetInvalidPaths returns a slice of paths that are invalid for the given message type.
// Returns an empty slice if all paths are valid.
func (x *FieldMask) GetInvalidPaths(m proto.Message) []stringUse Cases
API Validation with Detailed Error Messages
Current Problem - No way to identify which specific paths are invalid:
req.Msg.FieldMask.Normalize()
if !req.Msg.FieldMask.IsValid(req.Msg) {
// ❌ Can only return generic error - no way to tell user which paths are wrong
return status.Error(codes.InvalidArgument, "invalid field mask")
}With GetInvalidPaths - Provide specific feedback:
req.Msg.FieldMask.Normalize()
if invalidPaths := req.Msg.FieldMask.GetInvalidPaths(req.Msg); len(invalidPaths) > 0 {
// ✅ Tell user exactly which paths are problematic
return status.Errorf(codes.InvalidArgument,
"invalid field mask paths: %v", invalidPaths)
// Error: "invalid field mask paths: [user.invalid_field, metadata.bad_path]"
}Implementation
The implementation would reuse the existing numValidPaths function internally:
func (x *FieldMask) GetInvalidPaths(m proto.Message) []string {
if x == nil {
return nil
}
paths := x.GetPaths()
numValid := numValidPaths(m, paths)
if numValid == len(paths) {
return nil // all paths are valid
}
// Return the invalid paths (from numValid index onwards)
invalidPaths := make([]string, len(paths)-numValid)
copy(invalidPaths, paths[numValid:])
return invalidPaths
}Benefits
- Zero Breaking Changes - This is a pure addition that doesn't modify existing behavior
- Consistent with Existing Patterns - Follows the same validation logic as
IsValidandAppend - Minimal Performance Impact - Reuses existing validation logic, no additional reflection
- Developer Experience - Significantly improves debugging and error reporting capabilities
- Simple API - Clean, intuitive method signature that's easy to use
Alternatives Considered
- Modify
IsValidto return detailed information - Would break existing API - Add a separate validation result struct - More complex API for the common use case
- Use
Appendwith empty paths - Workaround that's not intuitive and doesn't provide all invalid paths
Backward Compatibility
This change is fully backward compatible as it only adds a new method without modifying existing functionality.
Reacted by Adam
Metadata
Metadata
Assignees
Labels
No labels
Activity
puellanivis commentedon Jun 23, 2025
🤔 Does
numValidPathsreorder thepathsinput? It seems it just doesreturn iwhen it finds an invalid path.If it doesn’t reorder the paths, then I don’t think the logic of your proposed implementation would actually hold? In that, it could return additional paths that are valid, but since they’re after an invalid path, you end up reporting them as if they were invalid.
P.S.: And if it does reorder the paths, then that would be quite the astonishment.
stapelberg commentedon Jun 24, 2025
(FWIW, this issue reads a lot like it was generated by an LLM, so I won’t spend time on this until the author replies in their own human voice to @puellanivis’s question.)
s4s7 commentedon Jun 25, 2025
Thank you for your feedback!
Here's the corrected implementation that properly checks each path individually.
However, this approach has performance implications due to nested loops and multiple descriptor retrievals.
Here's a more efficient version.
I can certainly refactor and add test code.
What do you think of this approach?