{"record":{"id":"16315a81aa37a37d","repo":"grpc/grpc-go","slug":"rls-bad-routelookupconfig-proto-v-v","errorCode":null,"errorMessage":"rls: bad RouteLookupConfig proto %+v: %v","messagePattern":"rls: bad RouteLookupConfig proto %\\+v: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/rls/config.go","lineNumber":158,"sourceCode":"//\t- childPolicy:\n//\t  - must find a valid child policy with a valid config\n//\n//\t- childPolicyConfigTargetFieldName:\n//\t  - must be set and non-empty\nfunc (rlsBB) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {\n\tif logger.V(2) {\n\t\tlogger.Infof(\"Received JSON service config: %v\", pretty.ToJSON(c))\n\t}\n\n\tcfgJSON := &lbConfigJSON{}\n\tif err := json.Unmarshal(c, cfgJSON); err != nil {\n\t\treturn nil, fmt.Errorf(\"rls: json unmarshal failed for service config %+v: %v\", string(c), err)\n\t}\n\n\tm := protojson.UnmarshalOptions{DiscardUnknown: true}\n\trlsProto := &rlspb.RouteLookupConfig{}\n\tif err := m.Unmarshal(cfgJSON.RouteLookupConfig, rlsProto); err != nil {\n\t\treturn nil, fmt.Errorf(\"rls: bad RouteLookupConfig proto %+v: %v\", string(cfgJSON.RouteLookupConfig), err)\n\t}\n\tlbCfg, err := parseRLSProto(rlsProto)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif sc := string(cfgJSON.RouteLookupChannelServiceConfig); sc != \"\" {\n\t\tparsed := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(sc)\n\t\tif parsed.Err != nil {\n\t\t\treturn nil, fmt.Errorf(\"rls: bad control channel service config %q: %v\", sc, parsed.Err)\n\t\t}\n\t\tlbCfg.controlChannelServiceConfig = sc\n\t}\n\n\tif cfgJSON.ChildPolicyConfigTargetFieldName == \"\" {\n\t\treturn nil, fmt.Errorf(\"rls: childPolicyConfigTargetFieldName field is not set in service config %+v\", string(c))\n\t}\n\tname, config, err := parseChildPolicyConfigs(cfgJSON.ChildPolicy, cfgJSON.ChildPolicyConfigTargetFieldName)","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/rls/config.go#L140-L176","documentation":"Returned by rls.ParseConfig (config.go:155-159) when the routeLookupConfig bytes cannot be unmarshalled via protojson into rlspb.RouteLookupConfig (with DiscardUnknown=true). This means the JSON is valid JSON but does not match the RouteLookupConfig proto schema (wrong field names, bad nested structure, invalid enum/Duration encoding). The %+v is the raw routeLookupConfig string.","triggerScenarios":"routeLookupConfig is valid JSON but uses field names that are not in grpc.lookup.v1.RouteLookupConfig, or encodes a duration/enum in a non-canonical way that protojson rejects.","commonSituations":"Control plane translating RLS config with a stale or wrong proto. Hand-writing the proto JSON with camelCase vs snake_case confusion (protojson accepts both, but typos are rejected). Including unknown fields (allowed due to DiscardUnknown, so this is usually a real schema mismatch).","solutions":["Compare the routeLookupConfig JSON against the grpc.lookup.v1.RouteLookupConfig proto definition; fix mismatched field names/types.","For google.protobuf.Duration fields use the \"Ns\" or seconds+nanos JSON form protojson expects.","Regenerate the config from a known-good RouteLookupConfig proto instance (protojson.Marshal) and diff.","Check the xDS control plane's RLS proto version against the client's."],"exampleFix":"// before: routeLookupConfig with wrong duration encoding or bad field name\nrlc := `{ \"lookupService\": \"rls.example\", \"grpcKeybuilders\": [...], \"maxAge\": \"5m\" }` // 'maxAge' wrong shape\n\n// after: build it from a typed proto and marshal\nb, _ := protojson.Marshal(&rlspb.RouteLookupConfig{ LookupService: \"rls.example\", MaxAge: durationpb.New(5*time.Minute) })","handlingStrategy":"validation","validationCode":"import \"google.golang.org/protobuf/encoding/protojson\"\nimport rlsproto \"...grpc_lookup_v1\"\n\nfunc validateRouteLookupConfig(raw []byte) error {\n    var p rlsproto.RouteLookupConfig\n    return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, &p)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build routeLookupConfig from a typed RouteLookupConfig proto and marshal with protojson.","Use canonical protojson Duration/enum encodings.","Keep the client's RLS proto version in sync with the control plane."],"tags":["go","grpc","load-balancing","rls","config","protobuf"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}