{"record":{"id":"c36db7ea38639b20","repo":"vitessio/vitess","slug":"failed-to-unmarshal-staticfile-config-from-json","errorCode":null,"errorMessage":"failed to unmarshal staticfile config from json: %w","messagePattern":"failed to unmarshal staticfile config from json: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/cluster/discovery/discovery_json.go","lineNumber":84,"sourceCode":"\tVtctlds []*JSONVtctldConfig `json:\"vtctlds,omitempty\"`\n}\n\n// JSONVTGateConfig contains host and tag information for a single VTGate in a cluster.\ntype JSONVTGateConfig struct {\n\tHost *vtadminpb.VTGate `json:\"host\"`\n\tTags []string          `json:\"tags\"`\n}\n\n// JSONVtctldConfig contains a host and tag information for a single\n// Vtctld in a cluster.\ntype JSONVtctldConfig struct {\n\tHost *vtadminpb.Vtctld `json:\"host\"`\n\tTags []string          `json:\"tags\"`\n}\n\nfunc (d *JSONDiscovery) parseConfig(bytes []byte) error {\n\tif err := json.Unmarshal(bytes, &d.config); err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal staticfile config from json: %w\", err)\n\t}\n\n\td.gates.byName = make(map[string]*vtadminpb.VTGate, len(d.config.VTGates))\n\td.gates.byTag = make(map[string][]*vtadminpb.VTGate)\n\n\t// Index the gates by name and by tag for easier lookups\n\tfor _, gate := range d.config.VTGates {\n\t\td.gates.byName[gate.Host.Hostname] = gate.Host\n\n\t\tfor _, tag := range gate.Tags {\n\t\t\td.gates.byTag[tag] = append(d.gates.byTag[tag], gate.Host)\n\t\t}\n\t}\n\n\td.vtctlds.byName = make(map[string]*vtadminpb.Vtctld, len(d.config.Vtctlds))\n\td.vtctlds.byTag = make(map[string][]*vtadminpb.Vtctld)\n\n\t// Index the vtctlds by name and by tag for easier lookups","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/cluster/discovery/discovery_json.go#L66-L102","documentation":"JSONDiscovery.parseConfig loads the vtadmin cluster's static/dynamic JSON config file into its in-memory config struct. If json.Unmarshal fails (malformed JSON or a type mismatch, e.g. a string where an array is expected), the error is wrapped and returned. This runs both for NewStaticFile at startup and for NewDynamic file-watch reloads.","triggerScenarios":"Calling cluster.NewStaticFile or NewDynamic with a JSON file that is syntactically invalid, uses the wrong types for known fields (host, tags, vtgates), or has unexpected values like a number for a string field.","commonSituations":"Hand-edited discovery JSON with a trailing comma or missing quote; automated tooling wrote YAML into a .json file; field type changed (tags given as a string instead of an array); truncated file from a failed write.","solutions":["Validate the JSON file with a linter (jq, jsonlint) and fix the syntax/type error reported there","Cross-check each field's type against the discovery JSON schema (host must be a vtctld object, tags an array of strings)","Restore the file from a known-good backup or regenerate it"],"exampleFix":"// before (broken)\n{\"vtctlds\": {\"host\": \"vtctld1\", \"tags\": \"primary\"}}\n// after\n{\"vtctlds\": {\"host\": \"vtctld1\", \"tags\": [\"primary\"]}}","handlingStrategy":"validation","validationCode":"var probe map[string]any\nif err := json.Unmarshal(bytes, &probe); err != nil {\n\treturn fmt.Errorf(\"discovery config is not valid JSON: %w\", err)\n}\n// optionally json.Valid(bytes) as a cheap pre-check\nif !json.Valid(bytes) {\n\treturn errors.New(\"discovery config file contains invalid JSON\")\n}","typeGuard":"func isValidDiscoveryConfig(b []byte) bool {\n\tvar d discoveryConfig\n\treturn json.Unmarshal(b, &d) == nil && d.VTGates != nil\n}","tryCatchPattern":"if err := json.Unmarshal(data, &cfg); err != nil {\n\tvar typeErr *json.UnmarshalTypeError\n\tif errors.As(err, &typeErr) {\n\t\tlog.Errorf(\"type mismatch at %s (want %s): %v\", typeErr.Field, typeErr.Type, typeErr)\n\t}\n\treturn err\n}","preventionTips":["Validate the JSON config with jq or a schema linter before deployment","Keep tags as JSON arrays, never strings","Generate the file programmatically instead of hand-editing","Write files atomically (temp file + rename) so reloads never see truncated JSON"],"tags":["go","vtadmin","json","config","unmarshal"],"backgroundTag":"invalid-json-config","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}