grpc/grpc-go · error

failed to marshal built priority config struct into json

Error message

failed to marshal built priority config struct into json: %v

What it means

Returned by buildPriorityConfigJSON (configbuilder.go:103) when json.Marshal(pc) fails on the priority.LBConfig struct that was just built successfully by buildPriorityConfig. The %v is the JSON marshalling error. Since the struct was constructed in-memory from validated types, a marshal failure indicates an internal type issue (e.g., a channel or function field, or a recursive struct). This is an internal invariant violation — the message is defensive.

Solutions

  1. Check for version consistency across grpc-go packages in go.mod.
  2. Inspect the wrapped %v — json.Marshal errors typically name the offending field/type.
  3. If you forked any of the config structs, ensure all fields are JSON-compatible.
  4. Report as a bug to grpc-go if using unmodified stock packages.
Defensive patterns

Strategy: try-catch

Try / catch

// Internal invariant violation — json.Marshal on an in-memory struct.
// Not actionable by user code; indicates version skew or code bug.
// Check: go list -m all | grep grpc — all entries should match.

Prevention

When it happens

Trigger: buildPriorityConfig returns a valid *priority.LBConfig (line 97), and json.Marshal is called on it (line 101). json.Marshal fails only if the struct contains unmarshallable types or hits a marshaler that errors. The priority.LBConfig and its children (clusterimpl.LBConfig, outlierdetection.LBConfig, internalserviceconfig.BalancerConfig) are all plain data structs with json tags, so marshal failure is unexpected.

Common situations: A code change that introduced an unmarshallable field type into the config struct tree. Version skew where the struct definition and the marshalling expectations diverge. Extremely unlikely in stock grpc-go.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/417ec260b4a98685. Report an issue: GitHub.

Appendix: source

Thrown at internal/xds/balancer/cdsbalancer/configbuilder.go:103

//	          ┌────────┐
//	          │priority│
//	          └┬──────┬┘
//	           │      │
//	┌──────────▼─┐  ┌─▼──────────┐
//	│cluster_impl│  │cluster_impl│
//	└──────┬─────┘  └─────┬──────┘
//	       │              │
//	┌──────▼─────┐  ┌─────▼──────┐
//	│xDSLBPolicy │  │xDSLBPolicy │ (Locality and Endpoint picking layer)
//	└────────────┘  └────────────┘
func buildPriorityConfigJSON(priorities []*priorityConfig, xdsLBPolicy *internalserviceconfig.BalancerConfig) ([]byte, []resolver.Endpoint, error) {
	pc, endpoints, err := buildPriorityConfig(priorities, xdsLBPolicy)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to build priority config: %v", err)
	}
	ret, err := json.Marshal(pc)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal built priority config struct into json: %v", err)
	}
	return ret, endpoints, nil
}

func buildPriorityConfig(priorities []*priorityConfig, xdsLBPolicy *internalserviceconfig.BalancerConfig) (*priority.LBConfig, []resolver.Endpoint, error) {
	var (
		retConfig    = &priority.LBConfig{Children: make(map[string]*priority.Child)}
		retEndpoints []resolver.Endpoint
	)
	for _, p := range priorities {
		clusterUpdate := p.clusterConfig.Cluster
		switch clusterUpdate.ClusterType {
		case xdsresource.ClusterTypeEDS:
			names, configs, endpoints, err := buildClusterImplConfigForEDS(p.childNameGen, p.clusterConfig, xdsLBPolicy)
			if err != nil {
				return nil, nil, err
			}
			retConfig.Priorities = append(retConfig.Priorities, names...)

View on GitHub (pinned to 0c51461d27)