go-kratos/kratos · error

enum %q is not registered

Error message

enum %q is not registered

What it means

Form-binding decoder error for enum-typed fields: protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()) returned protoregistry.NotFound, meaning the enum's Go type is not registered in the binary's global type registry. The decoder needs the registered Go enum (not just the descriptor) to look up value names/numbers, so an unlinked enum type makes every enum value for that field fail with this error.

Source

Thrown at encoding/form/proto_decode.go:169

		return fmt.Errorf("parsing map value %q: %w", fd.FullName().Name(), err)
	}
	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 {

View on GitHub (pinned to 668db92c2c)

Solutions

  1. Import the generated package containing the enum (even a blank import _ "your/repo/api/enumpkg") so its init() registers the type in GlobalTypes
  2. Regenerate protobuf code for the API package and make sure the enum file is compiled into this binary (check go.mod replace directives point at the right tree)
  3. Verify with a tiny test that protoregistry.GlobalTypes.FindEnumByName("pkg.EnumName") succeeds inside the failing binary
  4. If enums come from a third-party proto, ensure that dependency's Go module is a direct import, not merely transitive

Example fix

// before: enum lookup fails -> enum "api.Status" is not registered
import _ "your/repo/api/types" // missing in this binary

// after: ensure the generated enum package is imported where binding happens
import _ "your/repo/api/types" // registers api.Status via init()
Defensive patterns

Strategy: validation

Validate before calling

// Startup check: all enums referenced by the request types must be registered
func enumsRegistered(msgs ...protoreflect.Message) error {
	for _, m := range msgs {
		var err error
		m.Descriptor().Fields().Range(func(fd protoreflect.FieldDescriptor) bool {
			if fd.Kind() == protoreflect.EnumKind {
				_, e := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName())
				if e != nil {
				err = fmt.Errorf("enum %q: %w", fd.Enum().FullName(), e)
				return false
				}
			}
			return true
		})
		if err != nil {
			return err
		}
	}
	return nil
}

Type guard

func enumRegistered(fullName protoreflect.FullName) bool {
	_, err := protoregistry.GlobalTypes.FindEnumByName(fullName)
	return !errors.Is(err, protoregistry.NotFound)
}

Try / catch

if err := binding.BindQuery(msg, q); err != nil {
	if strings.Contains(err.Error(), "is not registered") {
		// build/link problem, not user input: fail loudly, no retry
		log.Error("enum type missing from binary", "err", err)
		return errors.InternalServer("ENUM_NOT_LINKED", err.Error())
	}
}

Prevention

When it happens

Trigger: Binding a form/query with a value for an enum field whose generated Go enum package is not linked into the binary: using protoc-generated descriptors via dynamic messages or reflection without importing the generated enum package; enum types registered under a forked/renamed package so FullName lookup misses; code stripped by dead-code elimination in minimal builds (rare in Go, common after generated files are deleted or moved).

Common situations: Mono-repo where the API package with generated enums is only imported by another binary, not this one; switching from static generated types to dynamic descriptors; regenerating protos into a new Go package path and stale registration; shading/renaming packages in CI.

Related errors


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/f2e6c3d564c0e3e7. Report an issue: GitHub.