{"record":{"id":"c6e9bc0caaea7b05","repo":"grpc/grpc-go","slug":"recovered-from-panic-during-resource-parsing-reso","errorCode":null,"errorMessage":"recovered from panic during resource parsing, resource: %v, panic: %v","messagePattern":"recovered from panic during resource parsing, resource: (.+?), panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/clients/xdsclient/channel.go","lineNumber":260,"sourceCode":"\ttimestamp := time.Now()\n\tmd := xdsresource.UpdateMetadata{\n\t\tVersion:   resp.version,\n\t\tTimestamp: timestamp,\n\t}\n\topts := &DecodeOptions{\n\t\tConfig:       xc.clientConfig,\n\t\tServerConfig: xc.serverConfig,\n\t}\n\n\ttopLevelErrors := make([]error, 0)          // Tracks deserialization errors, where we don't have a resource name.\n\tperResourceErrors := make(map[string]error) // Tracks resource validation errors, where we have a resource name.\n\tret := make(map[string]dataAndErrTuple)     // Return result, a map from resource name to either resource data or error.\n\tfor _, r := range resp.resources {\n\t\tresult, err := func() (res *DecodeResult, err error) {\n\t\t\tdefer func() {\n\t\t\t\tif envconfig.XDSRecoverPanicInResourceParsing {\n\t\t\t\t\tif p := recover(); p != nil {\n\t\t\t\t\t\terr = fmt.Errorf(\"recovered from panic during resource parsing, resource: %v, panic: %v\", r, p)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\treturn rType.Decoder.Decode(NewAnyProto(r), *opts)\n\t\t}()\n\n\t\t// Name field of the result is left unpopulated only when resource\n\t\t// deserialization fails.\n\t\tname := \"\"\n\t\tif result == nil && err == nil {\n\t\t\txc.logger.Errorf(\"Decode() returned nil result and nil error for resource: %v\", r)\n\t\t\tcontinue\n\t\t}\n\t\tif result != nil {\n\t\t\tname = xdsresource.ParseName(result.Name).String()\n\t\t}\n\t\tif err == nil {\n\t\t\tret[name] = dataAndErrTuple{Resource: result.Resource}","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/clients/xdsclient/channel.go#L242-L278","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// The library already wraps the panic as an error. In a custom Decoder:\ndefer func() {\n    if r := recover(); r != nil {\n        err = fmt.Errorf(\"decoder panic: %v\", r)\n    }\n}()","preventionTips":["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."],"tags":["xds","panic","decoder","resilience","configuration"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}