go-kratos/kratos · error
failed to look up enum: %w
Error message
failed to look up enum: %w
What it means
Form-binding decoder error for enum fields where the global registry lookup failed with an error other than protoregistry.NotFound - FindEnumByName returned a real error. In practice this is the protoregistry conflict path (e.g. protoregistry.DuplicateRegistration) or a registry-in-an-inconsistent-state failure, wrapped verbatim with %w so the original error text is visible.
Source
Thrown at encoding/form/proto_decode.go:171
mp.Set(key.MapKey(), value)
return nil
}
func parseField(fd protoreflect.FieldDescriptor, value string) (protoreflect.Value, error) {
switch fd.Kind() {
case protoreflect.BoolKind:
v, err := strconv.ParseBool(value)
if err != nil {
return protoreflect.Value{}, err
}
return protoreflect.ValueOfBool(v), nil
case protoreflect.EnumKind:
enum, err := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName())
switch {
case errors.Is(err, protoregistry.NotFound):
return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fd.Enum().FullName())
case err != nil:
return protoreflect.Value{}, fmt.Errorf("failed to look up enum: %w", err)
}
v := enum.Descriptor().Values().ByName(protoreflect.Name(value))
if v == nil {
i, err := strconv.ParseInt(value, 10, 32) //nolint:mnd
if err != nil {
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value)
}
v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i))
if v == nil {
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value)
}
}
return protoreflect.ValueOfEnum(v.Number()), nil
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
v, err := strconv.ParseInt(value, 10, 32) //nolint:mnd
if err != nil {
return protoreflect.Value{}, err
}View on GitHub (pinned to 668db92c2c)
Solutions
- Read the wrapped error - DuplicateRegistration names the conflicting full name; then audit go.mod (go mod graph / go list -m all) for two sources of the same generated package
- Remove or rename one of the conflicting generated packages (keep either the fork or the upstream, not both) and use go.mod replace to unify on a single copy
- Regenerate protos with consistent go_package options so full names map to one Go package
- Add a startup smoke test that calls protoregistry.GlobalTypes.FindEnumByName for your enums to catch conflicts before traffic
Example fix
# before: both linked -> failed to look up enum: duplicate registration .../Status go.mod: require github.com/org/api v1.2.3 replace github.com/org/api => ./forks/api-copy # after: single source of the generated enums require github.com/org/api v1.2.3 # no replace to a duplicate copy
Defensive patterns
Strategy: validation
Validate before calling
// Detect duplicate enum registration early in a test
func TestNoEnumConflicts(t *testing.T) {
protoregistry.GlobalTypes.RangeEnumsByMessage(func(protoreflect.EnumType) bool { return true })
// plus: register a second copy in a fork and expect DuplicateRegistration
} Try / catch
if err := binding.BindQuery(msg, q); err != nil {
if strings.Contains(err.Error(), "failed to look up enum") {
log.Error("protoregistry conflict or failure", "err", err) // wrapped cause names the duplicate
return errors.InternalServer("ENUM_REGISTRY", err.Error())
}
} Prevention
- Never link two copies of the same generated proto package (fork + upstream) in one binary
- Unify generated code paths with a single go.mod replace during migrations
- Check go list -m all for duplicate module paths providing identical proto packages in CI
- Run the enum registration smoke test in every build environment
When it happens
Trigger: Two different Go packages registering an enum with the same full name in GlobalTypes - typically the same proto generated twice into different Go packages and both linked into one binary (vendored fork + original, or api package copied between module paths); or a custom registry injected with conflicting entries. The switch at proto_decode.go:170-172 separates NotFound (error 51) from these other failures.
Common situations: Forking an api/proto repo and importing both fork and upstream in the same service; go.mod replace pointing at a copy while a transitive dependency still pulls the original; generated code duplicated across major versions of an API module; CI builds that vendor two generations of the same proto.
Related errors
- enum %q is not registered
- %q is not a valid value
- invalid path: %q is not a message
- field already set for oneof %q
- too many values for field %q: %s
AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16).
Data as JSON: /api/errors/2b42c0d9a88eb354.
Report an issue: GitHub.