grpc/grpc-go · error
recovered from panic during resource parsing, resource
Error message
recovered from panic during resource parsing, resource: %v, panic: %v
What it means
Produced by a recover() guard in xdsChannel.decodeResponse (channel.go:260), enabled only when GRPC_XDS_RECOVER_PANIC_IN_RESOURCE_PARSING (envconfig.XDSRecoverPanicInResourceParsing) is on. If a ResourceType.Decoder implementation panics while decoding a resource, the panic is converted to this error and the resource is treated as a NACKed top-level error rather than crashing the process.
Solutions
- Inspect the panic value and stack in the logs - it identifies the decoder and line that panicked.
- Fix the Decoder implementation to handle malformed input without panicking (return an error instead).
- If the decoder is third-party, upgrade or pin a version that handles the resource safely.
- Keep XDS_RECOVER_PANIC_IN_RESOURCE_PARSING enabled in production so a single bad resource cannot crash the client.
Defensive patterns
Strategy: try-catch
Try / catch
// The library already wraps the panic as an error. In a custom Decoder:
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("decoder panic: %v", r)
}
}() Prevention
- Keep envconfig.XDSRecoverPanicInResourceParsing enabled in production.
- Write fuzz tests for custom ResourceType.Decoder implementations.
- Return errors instead of panicking from Decode, even on malformed input.
When it happens
Trigger: rType.Decoder.Decode(NewAnyProto(r), *opts) panics - e.g. a custom ResourceType decoder hits a nil pointer, indexes out of range, or asserts a bad type. The deferred recover catches it and returns the formatted error, which then lands in topLevelErrors and causes the response to be NACKed.
Common situations: Custom ResourceType implementation with a bug; a malformed Any payload triggers an unchecked assumption in a vendored decoder; version mismatch where the decoder receives a proto it was not built for.
Related errors
- authority not found in the config for resource
- extauthz: empty grpc_service provided in config
- extauthz: missing default_value in deny_at_disable
- extauthz: missing default_value in filter_enabled
- grpctransport: config
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/c6e9bc0caaea7b05.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/clients/xdsclient/channel.go:260
timestamp := time.Now()
md := xdsresource.UpdateMetadata{
Version: resp.version,
Timestamp: timestamp,
}
opts := &DecodeOptions{
Config: xc.clientConfig,
ServerConfig: xc.serverConfig,
}
topLevelErrors := make([]error, 0) // Tracks deserialization errors, where we don't have a resource name.
perResourceErrors := make(map[string]error) // Tracks resource validation errors, where we have a resource name.
ret := make(map[string]dataAndErrTuple) // Return result, a map from resource name to either resource data or error.
for _, r := range resp.resources {
result, err := func() (res *DecodeResult, err error) {
defer func() {
if envconfig.XDSRecoverPanicInResourceParsing {
if p := recover(); p != nil {
err = fmt.Errorf("recovered from panic during resource parsing, resource: %v, panic: %v", r, p)
}
}
}()
return rType.Decoder.Decode(NewAnyProto(r), *opts)
}()
// Name field of the result is left unpopulated only when resource
// deserialization fails.
name := ""
if result == nil && err == nil {
xc.logger.Errorf("Decode() returned nil result and nil error for resource: %v", r)
continue
}
if result != nil {
name = xdsresource.ParseName(result.Name).String()
}
if err == nil {
ret[name] = dataAndErrTuple{Resource: result.Resource}View on GitHub (pinned to 0c51461d27)