golangci/golangci-lint · error
can't read viper config
Error message
can't read viper config: %w
What it means
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.
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
Example fix
# before (invalid YAML)
issues:
exclude-dirs:
- vendor
# after
issues:
exclude-dirs:
- vendor Defensive patterns
Strategy: validation
Validate before calling
func validYAML(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return err
}
var v map[string]any
return yaml.Unmarshal(data, &v)
}
// run in CI before golangci-lint: validYAML(".golangci.yml") Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- can't unmarshal config by viper (flags, file)
- can't unmarshal config by viper (flags)
- failed to calculate config salt
- failed to JSON marshal config linter settings
- analyzer doesn't have settings
AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02).
Data as JSON: /api/errors/6eb81fa21dafa7c6.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/config/base_loader.go:168
searchPaths = append(searchPaths, home)
}
return searchPaths
}
func (l *BaseLoader) parseConfig() error {
if err := l.viper.ReadInConfig(); err != nil {
if _, ok := errors.AsType[viper.ConfigFileNotFoundError](err); ok {
// Load configuration from flags only.
err = l.viper.Unmarshal(l.cfg, customDecoderHook())
if err != nil {
return fmt.Errorf("can't unmarshal config by viper (flags): %w", err)
}
return nil
}
return fmt.Errorf("can't read viper config: %w", err)
}
err := l.setConfigDir()
if err != nil {
return err
}
// Load configuration from all sources (flags, file).
if err := l.viper.Unmarshal(l.cfg, customDecoderHook()); err != nil {
return fmt.Errorf("can't unmarshal config by viper (flags, file): %w", err)
}
if l.cfg.IsInternalTest() { // just for testing purposes: to detect config file usage
_, _ = fmt.Fprintln(logutils.StdOut, "test")
os.Exit(exitcodes.Success)
}
return nilView on GitHub (pinned to ed7a235d2d)