grpc/grpc-go · error

rls: json marshal failed for child policy config {%+v}: %v

Error message

rls: json marshal failed for child policy config {%+v}: %v

What it means

After injecting the dummy target field, RLS marshals the map[string]json.RawMessage back to JSON to hand to the child policy's ParseConfig. Because every value is already valid json.RawMessage and the key is a string, json.Marshal essentially cannot fail; this branch is a defensive guard. Hitting it indicates memory/type corruption or a custom RawMessage containing invalid bytes.

Source

Thrown at balancer/rls/config.go:315

		}
		parser, ok := builder.(balancer.ConfigParser)
		if !ok {
			return "", nil, fmt.Errorf("rls: childPolicy %q with config %q does not support config parsing", name, string(rawCfg))
		}

		// To validate child policy configs we do the following:
		// - unmarshal the raw JSON bytes of the child policy config into a map
		// - add an entry with key set to `target_field_name` and a dummy value
		// - marshal the map back to JSON and parse the config using the parser
		// retrieved previously
		var childConfig map[string]json.RawMessage
		if err := json.Unmarshal(rawCfg, &childConfig); err != nil {
			return "", nil, fmt.Errorf("rls: json unmarshal failed for child policy config %q: %v", string(rawCfg), err)
		}
		childConfig[targetFieldName], _ = json.Marshal(dummyChildPolicyTarget)
		jsonCfg, err := json.Marshal(childConfig)
		if err != nil {
			return "", nil, fmt.Errorf("rls: json marshal failed for child policy config {%+v}: %v", childConfig, err)
		}
		if _, err := parser.ParseConfig(jsonCfg); err != nil {
			return "", nil, fmt.Errorf("rls: childPolicy config validation failed: %v", err)
		}
		return name, childConfig, nil
	}
	return "", nil, fmt.Errorf("rls: invalid childPolicy config: no supported policies found in %+v", childPolicies)
}

func convertDuration(d *durationpb.Duration) (time.Duration, error) {
	if d == nil {
		return 0, nil
	}
	return d.AsDuration(), d.CheckValid()
}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Treat this as an internal bug: capture the exact config and gRPC-Go version and file an issue.
  2. Re-resolve/redeploy the service config fresh to rule out transient memory corruption.
  3. If running a fork, diff against upstream config.go around line 312-316.
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := rlsBB{}.ParseConfig(rawCfg); err != nil {
    if strings.Contains(err.Error(), "json marshal failed for child policy config") {
        // defensively unreachable; log and fall back to a known-good config
        logger.Errorw("internal RLS marshal failure", "err", err)
        rawCfg = knownGoodConfig
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: Effectively unreachable in normal operation. Theoretically: a json.RawMessage value in the map that is not valid JSON and confuses the encoder, or an internal panic recovered into an error path.

Common situations: Near-impossible via config alone; would only appear with a corrupted in-memory config, a fork that mutated RawMessage fields, or a Go runtime/encoding bug.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/af2bc199d28fa57d. Report an issue: GitHub.