{"record":{"id":"4d0b0e23cdaf5b15","repo":"grpc/grpc-go","slug":"failed-to-marshal-child-policy-target-q-v","errorCode":null,"errorMessage":"failed to marshal child policy target %q: %v","messagePattern":"failed to marshal child policy target %q: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/rls/balancer.go","lineNumber":459,"sourceCode":"\t\t\t// Default target has already been taken care of.\n\t\t\tcontinue\n\t\t}\n\t\tif err := b.buildAndPushChildPolicyConfigs(cpw.target, newCfg, ccs); err != nil {\n\t\t\tcpw.lamify(err)\n\t\t}\n\t}\n}\n\n// buildAndPushChildPolicyConfigs builds the final child policy configuration by\n// adding the `targetField` to the base child policy configuration received in\n// RLS LB policy configuration. The `targetField` is set to target and\n// configuration is pushed to the child policy through the BalancerGroup.\n//\n// Caller must hold lb.stateMu.\nfunc (b *rlsBalancer) buildAndPushChildPolicyConfigs(target string, newCfg *lbConfig, ccs *balancer.ClientConnState) error {\n\tjsonTarget, err := json.Marshal(target)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal child policy target %q: %v\", target, err)\n\t}\n\n\tconfig := newCfg.childPolicyConfig\n\ttargetField := newCfg.childPolicyTargetField\n\tconfig[targetField] = jsonTarget\n\tjsonCfg, err := json.Marshal(config)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal child policy config %+v: %v\", config, err)\n\t}\n\n\tparser, _ := b.childPolicyBuilder.(balancer.ConfigParser)\n\tparsedCfg, err := parser.ParseConfig(jsonCfg)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"childPolicy config parsing failed: %v\", err)\n\t}\n\n\tstate := balancer.ClientConnState{ResolverState: ccs.ResolverState, BalancerConfig: parsedCfg}\n\tb.logger.Infof(\"Pushing new state to child policy %q: %+v\", target, state)","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/rls/balancer.go#L441-L477","documentation":"Emitted from rlsBalancer.buildAndPushChildPolicyConfigs when encoding/json cannot marshal the RLS-resolved backend target string (json.Marshal(target) at balancer.go:458). json.Marshal on a Go string only fails when the string contains invalid UTF-8 (e.g. lone surrogates or an unpaired continuation byte), so in practice this fires only for a corrupt target returned by the Route Lookup Server or set in the config's defaultTarget. On failure the childPolicyWrapper is put into lame-duck mode via cpw.lamify(err), so RPCs routed to that target fail fast until a new valid target arrives.","triggerScenarios":"The RLS server returns a lookup response whose target field contains invalid UTF-8, or the service config's defaultTarget field contains invalid UTF-8 bytes. Also reachable if a hand-crafted RLS response in tests injects a malformed string. Triggered during UpdateClientConnState / config propagation when each child's target is marshaled before being injected into the child policy config.","commonSituations":"A misbehaving or compromised RLS server emitting garbage bytes in target fields; binary/null bytes accidentally embedded in defaultTarget in a hand-authored service config; corrupted xDS/RLS payload over the wire; test fixtures that build targets from raw byte slices.","solutions":["Inspect the RLS server response payload: dump the target field hex to confirm invalid UTF-8 and fix the server-side data source.","If defaultTarget is the source, correct the value in the service config / xDS RLS config to a valid UTF-8 backend address such as 'dns:///backend.example:443'.","Verify there is no proxy/middlebox corrupting the RLS gRPC response (check mTLS terminators, transcoding proxies).","Add server-side input validation in the RLS service so targets are validated RFC 3986 host:port strings before being returned."],"exampleFix":"// before: defaultTarget contains invalid bytes (e.g. lone surrogate)\n\"defaultTarget\": \"backend\\ud800example:443\"\n\n// after: valid UTF-8 target URI\n\"defaultTarget\": \"dns:///backend.example:443\"","handlingStrategy":"validation","validationCode":"// Validate an RLS-returned target before pushing it to a child policy.\nfunc validTarget(s string) bool {\n    if !utf8.ValidString(s) {\n        return false\n    }\n    // Must round-trip through encoding/json for a string.\n    b, err := json.Marshal(s)\n    return err == nil && len(b) >= 2\n}","typeGuard":"func isValidRLSTarget(t string) bool {\n    return utf8.ValidString(t) && json.Valid([]byte(strconv.Quote(t)))\n}","tryCatchPattern":null,"preventionTips":["Validate RLS server responses at the server side: target fields must be valid UTF-8 host[:port] strings.","Keep defaultTarget in the service config to a stable, literal URI such as 'dns:///host:port'.","In tests, construct targets via net.JoinHostPort rather than raw byte concatenation."],"tags":["rls","child-policy","json","marshalling","utf8"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}