grpc/grpc-go · critical

cannot register a nil Codec

Error message

cannot register a nil Codec

What it means

encoding.RegisterCodec (encoding/encoding.go:132) registers a custom message Codec in gRPC's global registry, keyed by its Name(). It panics with this message at line 133-135 when codec == nil, because a nil codec would later cause nil-pointer dereferences inside Marshal/Unmarshal on every RPC. gRPC fails fast at registration (init) time instead of at RPC time.

Solutions

  1. Construct the codec value before calling RegisterCodec so it is never nil.
  2. If your codec constructor can return an error, check it and skip registration (or log.Fatal) on failure instead of passing the nil result.
  3. Register in an init() block so initialization order is deterministic and the nil is obvious at startup.

Example fix

// before
var myCodec encoding.Codec
func init() { encoding.RegisterCodec(myCodec) } // panic: nil

// after
type jsonCodec struct{}
func (jsonCodec) Name() string                  { return "json" }
func (jsonCodec) Marshal(v any) ([]byte, error)  { /* ... */ return nil, nil }
func (jsonCodec) Unmarshal(d []byte, v any) error { /* ... */ return nil }
func init() { encoding.RegisterCodec(jsonCodec{}) }
Defensive patterns

Strategy: validation

Validate before calling

// Guard before registering a Codec.
func registerSafe(c encoding.Codec) {
    if c == nil {
        log.Print("skipping registration: nil codec")
        return
    }
    encoding.RegisterCodec(c)
}

Try / catch

// Panics are fatal by design; recover only in test/setup shims.
defer func() {
    if r := recover(); r != nil {
        log.Fatalf("codec registration failed: %v", r)
    }
}()
encoding.RegisterCodec(c)

Prevention

When it happens

Trigger: Calling encoding.RegisterCodec(nil) directly; registering a package-level `var c encoding.Codec` that was never assigned; passing the nil result of a constructor whose error the caller ignored (e.g. `c, _ := NewCodec(cfg); RegisterCodec(c)`).

Common situations: Writing a custom JSON/flatbuffers/avro codec and forgetting to instantiate it; conditional registration behind a build tag where the codec is only created in one path; a refactor leaving a dangling nil reference before the init() that registers it.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/6721d487ea7a82bc. Report an issue: GitHub.

Appendix: source

Thrown at encoding/encoding.go:134

var registeredCodecs = make(map[string]any)

// RegisterCodec registers the provided Codec for use with all gRPC clients and
// servers.
//
// The Codec will be stored and looked up by result of its Name() method, which
// should match the content-subtype of the encoding handled by the Codec.  This
// is case-insensitive, and is stored and looked up as lowercase.  If the
// result of calling Name() is an empty string, RegisterCodec will panic. See
// Content-Type on
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
// more details.
//
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe.  If multiple Codecs are
// registered with the same name, the one registered last will take effect.
func RegisterCodec(codec Codec) {
	if codec == nil {
		panic("cannot register a nil Codec")
	}
	if codec.Name() == "" {
		panic("cannot register Codec with empty string result for Name()")
	}
	contentSubtype := strings.ToLower(codec.Name())
	registeredCodecs[contentSubtype] = codec
}

// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is
// registered for the content-subtype.
//
// The content-subtype is expected to be lowercase.
func GetCodec(contentSubtype string) Codec {
	c, _ := registeredCodecs[contentSubtype].(Codec)
	return c
}

View on GitHub (pinned to 0c51461d27)