{"record":{"id":"8c9a55dd151a8ff3","repo":"thanos-io/thanos","slug":"parsing-config-yaml-file-8c9a55","errorCode":null,"errorMessage":"parsing config YAML file","messagePattern":"parsing config YAML file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/receive/limiter_config.go","lineNumber":26,"sourceCode":"\n\t\"gopkg.in/yaml.v2\"\n\n\t\"github.com/thanos-io/thanos/pkg/clientconfig\"\n\t\"github.com/thanos-io/thanos/pkg/errors\"\n)\n\n// RootLimitsConfig is the root configuration for limits.\ntype RootLimitsConfig struct {\n\t// WriteLimits hold the limits for writing data.\n\tWriteLimits WriteLimitsConfig `yaml:\"write\"`\n}\n\n// ParseRootLimitConfig parses the root limit configuration. Even though\n// the result is a pointer, it will only be nil if an error is returned.\nfunc ParseRootLimitConfig(content []byte) (*RootLimitsConfig, error) {\n\tvar root RootLimitsConfig\n\tif err := yaml.UnmarshalStrict(content, &root); err != nil {\n\t\treturn nil, errors.Wrapf(err, \"parsing config YAML file\")\n\t}\n\n\tif root.WriteLimits.GlobalLimits.MetaMonitoringURL != \"\" {\n\t\tu, err := url.Parse(root.WriteLimits.GlobalLimits.MetaMonitoringURL)\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"parsing meta-monitoring URL\")\n\t\t}\n\n\t\t// url.Parse might pass a URL with only path, so need to check here for scheme and host.\n\t\t// As per docs: https://pkg.go.dev/net/url#Parse.\n\t\tif u.Host == \"\" || u.Scheme == \"\" {\n\t\t\treturn nil, errors.Newf(\"%s is not a valid meta-monitoring URL (scheme: %s,host: %s)\", u, u.Scheme, u.Host)\n\t\t}\n\t\troot.WriteLimits.GlobalLimits.metaMonitoringURL = u\n\t}\n\n\t// Set default query if none specified.\n\tif root.WriteLimits.GlobalLimits.MetaMonitoringLimitQuery == \"\" {","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/receive/limiter_config.go#L8-L44","documentation":"ParseRootLimitConfig reads a root limit-config YAML file and unmarshals it with yaml.UnmarshalStrict. This error wraps any YAML parsing or schema-mismatch failure, so the original yaml error (with line/column info) is preserved underneath. UnmarshalStrict means unknown fields in the file also trigger it.","triggerScenarios":"Calling ParseRootLimitConfig (or ParseLimitConfigContent, or runReceive when loading --receive.limits-config) with malformed YAML, wrong indentation/types, or unknown top-level fields since strict unmarshaling is used.","commonSituations":"Hand-edited limits config files with bad indentation; typos in field names like 'writelimts'; passing JSON where YAML is expected with tabs; kubernetes secret mounted config containing stray whitespace or an old schema no longer recognized after a Thanos upgrade.","solutions":["Check the wrapped yaml error in the error chain for the exact line/column and fix the YAML syntax","Remove or correct unknown fields — UnmarshalStrict rejects fields not present in RootLimitsConfig","Validate types: e.g. max_connection or limit fields must be integers, not strings","Run the file through a YAML linter and confirm top-level structure is writeLimits (with globalLimits etc.)","Verify the file mounted/passed to the component is the intended config (not a secret manifest or empty file)"],"exampleFix":"// before (invalid: unknown key + bad type)\nwriteLimits:\n  globalLimit:\n    max_head_series: \"100000\"\n// after\nwriteLimits:\n  globalLimits:\n    maxHeadSeries: 100000","handlingStrategy":"validation","validationCode":"// Go: pre-validate YAML before calling ParseRootLimitConfig\nif len(content) == 0 { return errors.New(\"limits config is empty\") }\nvar probe map[string]interface{}\nif err := yaml.Unmarshal(content, &probe); err != nil {\n    return fmt.Errorf(\"invalid YAML: %w\", err)\n}\n_, ok := probe[\"writeLimits\"]\nif !ok { return errors.New(\"missing writeLimits section\") }","typeGuard":"func hasKnownKeys(root map[string]interface{}) bool {\n    for k := range root {\n        if k != \"writeLimits\" { return false }\n    }\n    return true\n}","tryCatchPattern":"cfg, err := ParseRootLimitConfig(content)\nif err != nil {\n    var yErr yaml.TypeError\n    if errors.As(err, &yErr) { log.Fatalf(\"config schema error: %v\", err) }\n    return fmt.Errorf(\"loading limits config: %w\", err)\n}","preventionTips":["Lint the limits YAML in CI with a schema validator (kubeconform-style or yamllint + schema)","Pin a config template and review all field names against RootLimitsConfig","Test config load at deploy time with a canary before rollout","Beware UnmarshalStrict: any unknown key is fatal"],"tags":["yaml","config","parsing"],"backgroundTag":"yaml-parse-error","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}