grpc/grpc-go · critical
cannot register a nil CodecV2
Error message
cannot register a nil CodecV2
What it means
CodecV2 (encoding/encoding_v2.go:30) is the newer, memory-pool-aware codec interface whose Marshal returns and Unmarshal accepts a mem.BufferSlice for zero-copy buffer reuse. RegisterCodecV2 (line 63) panics at line 64-66 when codec == nil for the same reason RegisterCodec does: a nil CodecV2 would dereference during every RPC's (un)marshaling. If both a Codec and CodecV2 share a name, the V2 wins.
Solutions
- Construct the CodecV2 implementation before calling RegisterCodecV2.
- Prefer registering a CodecV2 over a Codec for the same name only when the implementation is fully initialized.
- Add a unit test that calls RegisterCodecV2 to surface a nil at test time, not in production init().
Example fix
// before
var c2 encoding.CodecV2
func init() { encoding.RegisterCodecV2(c2) } // panic: nil
// after
type protoCodecV2 struct{}
func (protoCodecV2) Name() string { return "proto" }
func (protoCodecV2) Marshal(v any) (mem.BufferSlice, error) { /* ... */ return nil, nil }
func (protoCodecV2) Unmarshal(d mem.BufferSlice, v any) error { /* ... */ return nil }
func init() { encoding.RegisterCodecV2(protoCodecV2{}) } Defensive patterns
Strategy: validation
Validate before calling
func registerV2Safe(c encoding.CodecV2) {
if c == nil {
log.Print("skipping V2 registration: nil codec")
return
}
encoding.RegisterCodecV2(c)
} Prevention
- Construct the CodecV2 implementation before calling RegisterCodecV2.
- When migrating from Codec to CodecV2, build the new struct in the same init() that registers it.
- Unit-test registration so a nil appears at test time.
When it happens
Trigger: Calling encoding.RegisterCodecV2(nil); registering a CodecV2 variable that was never initialized; passing the nil zero-value of a pointer-typed CodecV2 implementation.
Common situations: Migrating a custom codec from Codec to CodecV2 and forgetting to construct the new struct; conditional V2 registration where the construction path was skipped; refactor leaving a nil pointer before init().
Related errors
- cannot register CodecV2 with empty string result for Name()
- cannot register a nil Codec
- cannot register Codec with empty string result for Name()
- Cannot free freed buffer
- Cannot read freed buffer
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/ba147ffa7cc70a1d.
Report an issue: GitHub.
Appendix: source
Thrown at encoding/encoding_v2.go:65
// servers.
//
// The CodecV2 will be stored and looked up by result of its Name() method, which
// should match the content-subtype of the encoding handled by the CodecV2. This
// is case-insensitive, and is stored and looked up as lowercase. If the
// result of calling Name() is an empty string, RegisterCodecV2 will panic. See
// Content-Type on
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
// more details.
//
// If both a Codec and CodecV2 are registered with the same name, the CodecV2
// will be used.
//
// 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 RegisterCodecV2(codec CodecV2) {
if codec == nil {
panic("cannot register a nil CodecV2")
}
if codec.Name() == "" {
panic("cannot register CodecV2 with empty string result for Name()")
}
contentSubtype := strings.ToLower(codec.Name())
registeredCodecs[contentSubtype] = codec
}
// GetCodecV2 gets a registered CodecV2 by content-subtype, or nil if no CodecV2 is
// registered for the content-subtype.
//
// The content-subtype is expected to be lowercase.
func GetCodecV2(contentSubtype string) CodecV2 {
c, _ := registeredCodecs[contentSubtype].(CodecV2)
return c
}
View on GitHub (pinned to 0c51461d27)