vitessio/vitess · error
failed to unmarshal staticfile config from json: %w
Error message
failed to unmarshal staticfile config from json: %w
What it means
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.
Source
Thrown at go/vt/vtadmin/cluster/discovery/discovery_json.go:84
Vtctlds []*JSONVtctldConfig `json:"vtctlds,omitempty"`
}
// JSONVTGateConfig contains host and tag information for a single VTGate in a cluster.
type JSONVTGateConfig struct {
Host *vtadminpb.VTGate `json:"host"`
Tags []string `json:"tags"`
}
// JSONVtctldConfig contains a host and tag information for a single
// Vtctld in a cluster.
type JSONVtctldConfig struct {
Host *vtadminpb.Vtctld `json:"host"`
Tags []string `json:"tags"`
}
func (d *JSONDiscovery) parseConfig(bytes []byte) error {
if err := json.Unmarshal(bytes, &d.config); err != nil {
return fmt.Errorf("failed to unmarshal staticfile config from json: %w", err)
}
d.gates.byName = make(map[string]*vtadminpb.VTGate, len(d.config.VTGates))
d.gates.byTag = make(map[string][]*vtadminpb.VTGate)
// Index the gates by name and by tag for easier lookups
for _, gate := range d.config.VTGates {
d.gates.byName[gate.Host.Hostname] = gate.Host
for _, tag := range gate.Tags {
d.gates.byTag[tag] = append(d.gates.byTag[tag], gate.Host)
}
}
d.vtctlds.byName = make(map[string]*vtadminpb.Vtctld, len(d.config.Vtctlds))
d.vtctlds.byTag = make(map[string][]*vtadminpb.Vtctld)
// Index the vtctlds by name and by tag for easier lookupsView on GitHub (pinned to 01a25a7d17)
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
Example fix
// before (broken)
{"vtctlds": {"host": "vtctld1", "tags": "primary"}}
// after
{"vtctlds": {"host": "vtctld1", "tags": ["primary"]}} Defensive patterns
Strategy: validation
Validate before calling
var probe map[string]any
if err := json.Unmarshal(bytes, &probe); err != nil {
return fmt.Errorf("discovery config is not valid JSON: %w", err)
}
// optionally json.Valid(bytes) as a cheap pre-check
if !json.Valid(bytes) {
return errors.New("discovery config file contains invalid JSON")
} Type guard
func isValidDiscoveryConfig(b []byte) bool {
var d discoveryConfig
return json.Unmarshal(b, &d) == nil && d.VTGates != nil
} Try / catch
if err := json.Unmarshal(data, &cfg); err != nil {
var typeErr *json.UnmarshalTypeError
if errors.As(err, &typeErr) {
log.Errorf("type mismatch at %s (want %s): %v", typeErr.Field, typeErr.Type, typeErr)
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error parsing %s: %w
- range %d should be >= %d
- too big depth for the nested JSON; it exceeds %d
- unexpected character %q in JSON
- expected ',' or '}' in object, got %q
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/c36db7ea38639b20.
Report an issue: GitHub.