Description
FieldDescriptor is nil on violations for map values, which is problematic when attempting to handle violations by referring to the source field's protoreflect type.
Steps to Reproduce
https://github.com/haines/protovalidate-violation-field-descriptor
syntax = "proto3";
package example.v1;
import "buf/validate/validate.proto";
option go_package = "github.com/haines/protovalidate-violation-field-descriptor/gen/example/v1;examplev1";
enum Value {
VALUE_UNSPECIFIED = 0;
VALUE_A = 1;
VALUE_B = 2;
}
message Example {
Value value = 1 [(buf.validate.field).enum = {
in: [
1,
2
]
}];
map<string, Value> values = 2 [(buf.validate.field).map.values.enum = {
in: [
1,
2
]
}];
}
package main
import (
"errors"
"fmt"
"log"
"buf.build/go/protovalidate"
examplev1 "github.com/haines/protovalidate-violation-field-descriptor/gen/example/v1"
)
func main() {
err := protovalidate.Validate(&examplev1.Example{
Value: examplev1.Value_VALUE_UNSPECIFIED,
Values: map[string]examplev1.Value{"foo": examplev1.Value_VALUE_UNSPECIFIED},
})
validationErr, ok := errors.AsType[*protovalidate.ValidationError](err)
if !ok {
log.Fatalln(err)
}
for _, violation := range validationErr.Violations {
fmt.Println(violation)
fmt.Println(violation.FieldDescriptor)
fmt.Println()
}
}
$ go run github.com/haines/protovalidate-violation-field-descriptor@v0.0.0-20260615182102-eacba7874c96
value: must be in list [1, 2]
FieldDescriptor{Syntax: proto3, FullName: example.v1.Example.value, Number: 1, Cardinality: optional, Kind: enum, HasJSONName: true, JSONName: "value", Enum: example.v1.Value}
values["foo"]: must be in list [1, 2]
<nil>
Expected Behavior
violation.FieldDescriptor is set to the map values' field descriptor (i.e. what you get by calling MapValue() on the parent field descriptor).
Actual Behavior
violation.FieldDescriptor is nil.
Screenshots/Logs
N/A
Environment
- Operating System: N/A
- Version: N/A
- Compiler/Toolchain: N/A
- Protobuf Compiler & Version: buf v1.70.0, buf.build/protocolbuffers/go:v1.36.11
- Protovalidate Version: v1.2.0
Possible Solution
Additional Context
I am trying to work around #125 by creating a new error message for violations of enum.in rules.
I came up with the following
for _, violation := range validationErr.Violations {
if violation.RuleDescriptor.FullName() == "buf.validate.EnumRules.in" {
allValues := violation.FieldDescriptor.Enum().Values()
allowedValues := violation.RuleValue.List()
allowedNames := make([]string, allowedValues.Len())
for i := range allowedValues.Len() {
number := allowedValues.Get(i).Int()
desc := allValues.ByNumber(protoreflect.EnumNumber(number))
if desc == nil {
allowedNames[i] = strconv.FormatInt(number, 10)
} else {
allowedNames[i] = string(desc.Name())
}
}
slices.Sort(allowedNames)
violation.Proto.Message = proto.String(fmt.Sprintf("must be one of [%s]", strings.Join(allowedNames, ", ")))
}
}
This works nicely for enum fields in messages. However, it panics on enum-valued maps, because violation.FieldDescriptor is nil.
Description
FieldDescriptorisnilon violations for map values, which is problematic when attempting to handle violations by referring to the source field's protoreflect type.Steps to Reproduce
https://github.com/haines/protovalidate-violation-field-descriptor
Expected Behavior
violation.FieldDescriptoris set to the map values' field descriptor (i.e. what you get by callingMapValue()on the parent field descriptor).Actual Behavior
violation.FieldDescriptorisnil.Screenshots/Logs
N/A
Environment
Possible Solution
Additional Context
I am trying to work around #125 by creating a new error message for violations of
enum.inrules.I came up with the following
This works nicely for enum fields in messages. However, it panics on enum-valued maps, because
violation.FieldDescriptorisnil.