{"record":{"id":"d2410ca77bfbc5a9","repo":"grpc/grpc-go","slug":"xds-unable-to-unmarshal-lbconfig-s-error-v-d2410c","errorCode":null,"errorMessage":"xds: unable to unmarshal LBconfig: %s, error: %v","messagePattern":"xds: unable to unmarshal LBconfig: (.+?), error: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/balancer/outlierdetection/balancer.go","lineNumber":112,"sourceCode":"\tb.logger.Infof(\"Created\")\n\tb.child = synchronizingBalancerWrapper{lb: gracefulswitch.NewBalancer(b, bOpts)}\n\tgo b.run()\n\treturn b\n}\n\nfunc (bb) ParseConfig(s json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {\n\tlbCfg := &LBConfig{\n\t\t// Default top layer values as documented in A50.\n\t\tInterval:           iserviceconfig.Duration(10 * time.Second),\n\t\tBaseEjectionTime:   iserviceconfig.Duration(30 * time.Second),\n\t\tMaxEjectionTime:    iserviceconfig.Duration(300 * time.Second),\n\t\tMaxEjectionPercent: 10,\n\t}\n\n\t// This unmarshalling handles underlying layers sre and fpe which have their\n\t// own defaults for their fields if either sre or fpe are present.\n\tif err := json.Unmarshal(s, lbCfg); err != nil { // Validates child config if present as well.\n\t\treturn nil, fmt.Errorf(\"xds: unable to unmarshal LBconfig: %s, error: %v\", string(s), err)\n\t}\n\n\t// Note: in the xds flow, these validations will never fail. The xdsclient\n\t// performs the same validations as here on the xds Outlier Detection\n\t// resource before parsing resource into JSON which this function gets\n\t// called with. A50 defines two separate places for these validations to\n\t// take place, the xdsclient and this ParseConfig method. \"When parsing a\n\t// config from JSON, if any of these requirements is violated, that should\n\t// be treated as a parsing error.\" - A50\n\tswitch {\n\t// \"The google.protobuf.Duration fields interval, base_ejection_time, and\n\t// max_ejection_time must obey the restrictions in the\n\t// google.protobuf.Duration documentation and they must have non-negative\n\t// values.\" - A50\n\t// Approximately 290 years is the maximum time that time.Duration (int64)\n\t// can represent. The restrictions on the protobuf.Duration field are to be\n\t// within +-10000 years. Thus, just check for negative values.\n\tcase lbCfg.Interval < 0:","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/balancer/outlierdetection/balancer.go#L94-L130","documentation":"The outlier detection balancer's ParseConfig unmarshals the raw JSON into an LBConfig struct with fields for interval, base_ejection_time, max_ejection_time, max_ejection_percent, and sub-configs for success-rate ejection (sre) and failure-percentage ejection (fpe). This error fires when the JSON is syntactically invalid or field types are wrong. Outlier detection implements adaptive circuit-breaking based on backend health metrics.","triggerScenarios":"Triggered when the outlier detection balancer's ParseConfig is called (from xDS or directly) and json.Unmarshal fails on the raw JSON. In the xDS flow, the CDS balancer calls this with the outlier detection JSON from the cluster resource. The struct has specific defaults and validation for duration fields and numeric ranges.","commonSituations":"The management server sends malformed outlier detection JSON with wrong field types (e.g., string for a numeric field); the JSON uses incorrect field names or nesting; a protobuf-to-JSON serialization issue on the server produces structurally invalid JSON; a version mismatch where field names changed between protocol versions.","solutions":["Inspect the raw JSON in the error message (it's included as %s) to identify the specific malformation","Verify field names match the protobuf JSON mapping: interval, baseEjectionTime, maxEjectionTime, maxEjectionPercent, childPolicy, successRateEjection, failurePercentageEjection","Ensure duration fields are strings in Go duration format (e.g., \"10s\", \"30s\") and numeric fields are numbers","Verify the management server's outlier detection configuration matches gRFC A50","If the error comes from manual ParseConfig calls, validate the JSON schema against the expected format"],"exampleFix":"// before: malformed outlier detection config with wrong types\n{\"interval\": 10, \"maxEjectionPercent\": \"10\"}\n// after: correct types (duration as string, percent as number)\n{\"interval\": \"10s\", \"maxEjectionPercent\": 10}","handlingStrategy":"validation","validationCode":"// Validate outlier detection config JSON before passing to ParseConfig\nfunc validateOutlierDetectionConfig(raw json.RawMessage) error {\n    var cfg struct {\n        Interval           json.Number `json:\"interval\"`\n        BaseEjectionTime   json.Number `json:\"baseEjectionTime\"`\n        MaxEjectionTime    json.Number `json:\"maxEjectionTime\"`\n        MaxEjectionPercent json.Number `json:\"maxEjectionPercent\"`\n    }\n    if err := json.Unmarshal(raw, &cfg); err != nil {\n        return fmt.Errorf(\"invalid outlier detection JSON: %w\", err)\n    }\n    return nil\n}","typeGuard":"// Type guard for parsed outlier detection config\nfunc isOutlierDetectionConfig(cfg serviceconfig.LoadBalancingConfig) bool {\n    _, ok := cfg.(*outlierdetection.LBConfig)\n    return ok\n}","tryCatchPattern":null,"preventionTips":["Validate outlier detection JSON configs for correct types: durations as strings, percentages as numbers","Verify field names match protobuf JSON mapping (interval, baseEjectionTime, maxEjectionTime, maxEjectionPercent)","Test outlier detection configs from the management server against gRFC A50","Keep grpc-go version current for outlier detection config schema compatibility"],"tags":["xds","outlier-detection","json","config","balancer"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}