{"record":{"id":"6eb81fa21dafa7c6","repo":"golangci/golangci-lint","slug":"can-t-read-viper-config-w","errorCode":null,"errorMessage":"can't read viper config: %w","messagePattern":"can't read viper config: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/base_loader.go","lineNumber":168,"sourceCode":"\t\tsearchPaths = append(searchPaths, home)\n\t}\n\n\treturn searchPaths\n}\n\nfunc (l *BaseLoader) parseConfig() error {\n\tif err := l.viper.ReadInConfig(); err != nil {\n\t\tif _, ok := errors.AsType[viper.ConfigFileNotFoundError](err); ok {\n\t\t\t// Load configuration from flags only.\n\t\t\terr = l.viper.Unmarshal(l.cfg, customDecoderHook())\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't unmarshal config by viper (flags): %w\", err)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\treturn fmt.Errorf(\"can't read viper config: %w\", err)\n\t}\n\n\terr := l.setConfigDir()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Load configuration from all sources (flags, file).\n\tif err := l.viper.Unmarshal(l.cfg, customDecoderHook()); err != nil {\n\t\treturn fmt.Errorf(\"can't unmarshal config by viper (flags, file): %w\", err)\n\t}\n\n\tif l.cfg.IsInternalTest() { // just for testing purposes: to detect config file usage\n\t\t_, _ = fmt.Fprintln(logutils.StdOut, \"test\")\n\t\tos.Exit(exitcodes.Success)\n\t}\n\n\treturn nil","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/golangci/golangci-lint/blob/ed7a235d2d771152056fdc142a9855567c5796a9/pkg/config/base_loader.go#L150-L186","documentation":"parseConfig calls viper.ReadInConfig; if reading fails for reasons other than \"config file not found\" (parse errors, permission, IO), the error is wrapped as \"can't read viper config\" and config loading stops.","triggerScenarios":"A config file exists at the resolved location but viper cannot read/parse it: malformed YAML/JSON/TOML syntax, unreadable file permissions, unreadable config directory (setConfigDir side effects happen after), or a corrupted search path.","commonSituations":"Hand-edited .golangci.yml with indentation/tab mistakes; merge-conflict markers left in the file; root-owned config in CI containers; a stray .golangci.yaml in a parent directory with bad syntax.","solutions":["Validate the YAML/JSON config file syntax (yamllint or a YAML parser) and fix syntax errors","Check file permissions so the lint process can read the config file","Use `golangci-lint config path` to see which config file is being picked up; remove or fix stale ones","Read the wrapped %w error — viper reports the file and line of parse failures"],"exampleFix":"# before (invalid YAML)\nissues:\n\texclude-dirs:\n  - vendor\n# after\nissues:\n  exclude-dirs:\n    - vendor","handlingStrategy":"validation","validationCode":"func validYAML(path string) error {\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar v map[string]any\n\treturn yaml.Unmarshal(data, &v)\n}\n// run in CI before golangci-lint: validYAML(\".golangci.yml\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint your config YAML in CI (yamllint) before running golangci-lint","Watch for tabs — YAML forbids them for indentation","Clean merge-conflict markers before linting","Check `golangci-lint config path` to know which config file is loaded"],"tags":["golangci-lint","config","viper","yaml"],"backgroundTag":"config-file-parse-error","analyzedSha":"ed7a235d2d771152056fdc142a9855567c5796a9","analyzedAt":"2026-09-02T18:47:33.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}