grpc/grpc-go · error
invalid code
Error message
invalid code: %d
What it means
Returned by codes.Code.UnmarshalJSON when the JSON input is a numeric value that is >= _maxCode (17). gRPC status codes are only defined for 0 (OK) through 16 (Unauthenticated); any integer 17 or above is out of the valid range and cannot be mapped to a gRPC Code.
Solutions
- Map the external numeric code into a valid gRPC Code (0–16) before unmarshaling, e.g. convert HTTP 404 to codes.NotFound(5).
- If the JSON value is a string code name, send it as a quoted string (e.g. "NOT_FOUND") instead of a number.
- Validate the numeric range [0,16] before assigning it to a codes.Code to surface the problem at the right boundary.
Example fix
// before var c codes.Code json.Unmarshal([]byte(`404`), &c) // invalid code: 404 // after var c codes.Code = codes.NotFound // map HTTP 404 -> gRPC NotFound before encoding
Defensive patterns
Strategy: validation
Validate before calling
func parseNumericCode(b []byte) (codes.Code, error) {
n, err := strconv.ParseUint(string(b), 10, 32)
if err != nil {
return codes.OK, err
}
if n >= 17 {
return codes.OK, fmt.Errorf("code %d out of range [0,16]", n)
}
return codes.Code(n), nil
} Type guard
func isValidNumericCode(n uint32) bool { return n < 17 } Prevention
- Map external (HTTP/vendor) numeric codes into [0,16] before producing JSON for codes.Code.
- When interoperating, use string code names (quoted) that match the strToCode map exactly.
- Document the valid range at the boundary where foreign codes enter your system.
When it happens
Trigger: Calling json.Unmarshal on a *codes.Code with input like 17, 99, or 500 — i.e. a numeric JSON token (no quotes) whose value is outside [0, 16]. Also triggered by serializing a custom/unknown code from another system and deserializing it as a number.
Common situations: Interoperability with a non-gRPC system that uses its own numeric error codes that exceed the gRPC range, or a protobuf/JSON payload whose 'code' field was populated with an HTTP-style status code (e.g. 404) instead of a gRPC code.
Related errors
- invalid code
- nil receiver passed to UnmarshalJSON
- least-request: unable to unmarshal LBConfig
- pickfirst: unable to unmarshal LB policy config
- randomsubsetting: json.Unmarshal failed for configuration
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/8a8e78cc29ef59dc.
Report an issue: GitHub.
Appendix: source
Thrown at codes/codes.go:238
`"DATA_LOSS"`: DataLoss,
`"UNAUTHENTICATED"`: Unauthenticated,
}
// UnmarshalJSON unmarshals b into the Code.
func (c *Code) UnmarshalJSON(b []byte) error {
// From json.Unmarshaler: By convention, to approximate the behavior of
// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
// a no-op.
if string(b) == "null" {
return nil
}
if c == nil {
return fmt.Errorf("nil receiver passed to UnmarshalJSON")
}
if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil {
if ci >= _maxCode {
return fmt.Errorf("invalid code: %d", ci)
}
*c = Code(ci)
return nil
}
if jc, ok := strToCode[string(b)]; ok {
*c = jc
return nil
}
return fmt.Errorf("invalid code: %q", string(b))
}
View on GitHub (pinned to 0c51461d27)