{"record":{"id":"8e080ff950949fb0","repo":"grpc/grpc-go","slug":"expected-a-json-struct-with-one-entry-received-en","errorCode":null,"errorMessage":"expected a JSON struct with one entry; received entry %v at index %d","messagePattern":"expected a JSON struct with one entry; received entry (.+?) at index (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/balancer/gracefulswitch/config.go","lineNumber":56,"sourceCode":"func ChildName(l serviceconfig.LoadBalancingConfig) string {\n\treturn l.(*lbConfig).childBuilder.Name()\n}\n\n// ParseConfig parses a child config list and returns a LB config for the\n// gracefulswitch Balancer.\n//\n// cfg is expected to be a json.RawMessage containing a JSON array of LB policy\n// names + configs as the format of the \"loadBalancingConfig\" field in\n// ServiceConfig.  It returns a type that should be passed to\n// UpdateClientConnState in the BalancerConfig field.\nfunc ParseConfig(cfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {\n\tvar lbCfg []map[string]json.RawMessage\n\tif err := json.Unmarshal(cfg, &lbCfg); err != nil {\n\t\treturn nil, err\n\t}\n\tfor i, e := range lbCfg {\n\t\tif len(e) != 1 {\n\t\t\treturn nil, fmt.Errorf(\"expected a JSON struct with one entry; received entry %v at index %d\", e, i)\n\t\t}\n\n\t\tvar name string\n\t\tvar jsonCfg json.RawMessage\n\t\tfor name, jsonCfg = range e {\n\t\t}\n\n\t\tbuilder := balancer.Get(name)\n\t\tif builder == nil {\n\t\t\t// Skip unregistered balancer names.\n\t\t\tcontinue\n\t\t}\n\n\t\tparser, ok := builder.(balancer.ConfigParser)\n\t\tif !ok {\n\t\t\t// This is a valid child with no config.\n\t\t\treturn &lbConfig{childBuilder: builder}, nil\n\t\t}","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/balancer/gracefulswitch/config.go#L38-L74","documentation":"Returned by gracefulswitch.ParseConfig when unmarshalling the loadBalancingConfig JSON array. Each array element must be a JSON object with exactly one key (the LB policy name). If an element has zero keys (empty object) or two or more keys, the parser rejects it with this error. The gracefulswitch balancer uses this format to identify which child policy to activate.","triggerScenarios":"Calling gracefulswitch.ParseConfig() or balancergroup.ParseConfig() (which delegates to it at balancergroup.go:606) with a json.RawMessage whose parsed array contains an entry that is not a single-key map. For example, [{\"round_robin\":{},\"grpclb\":{}}] puts two policies in one entry, or [{}] has zero keys.","commonSituations":"A hand-edited or programmatically-generated service config JSON where multiple LB policies are placed inside a single array element instead of being split across separate elements. This commonly occurs when developers unfamiliar with the gRPC loadBalancingConfig format try to list fallback policies.","solutions":["Ensure each element of the loadBalancingConfig JSON array contains exactly one key mapping a policy name to its config object","Split multi-key entries into separate array elements: [{\"a\":{}},{\"b\":{}}] instead of [{\"a\":{},\"b\":{}}]","Validate the JSON structure by unmarshalling into []map[string]json.RawMessage and asserting len(entry)==1 before calling ParseConfig"],"exampleFix":"// before (malformed: two keys in one entry)\n\"loadBalancingConfig\": [{\"round_robin\":{}, \"weighted_target\":{}}]\n\n// after (correct: one key per entry)\n\"loadBalancingConfig\": [{\"round_robin\":{}}, {\"weighted_target\":{}}]","handlingStrategy":"validation","validationCode":"// Validate loadBalancingConfig JSON before calling gracefulswitch.ParseConfig\nfunc validateLBConfig(raw json.RawMessage) error {\n    var entries []map[string]json.RawMessage\n    if err := json.Unmarshal(raw, &entries); err != nil {\n        return fmt.Errorf(\"not a JSON array: %w\", err)\n    }\n    for i, e := range entries {\n        if len(e) != 1 {\n            return fmt.Errorf(\"entry at index %d has %d keys; expected exactly 1\", i, len(e))\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always validate the loadBalancingConfig JSON structure before passing it to ParseConfig","When generating service config programmatically, use a builder pattern that enforces one policy per array element","Run a JSON schema validator on service config before deployment"],"tags":["grpc","balancer","config","json","gracefulswitch"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}