golangci/golangci-lint · error
failed to JSON marshal config linter settings: %w
Error message
failed to JSON marshal config linter settings: %w
What it means
Inside computeConfigSalt, golangci-lint marshals cfg.Linters.Settings with yaml.Marshal; failure is wrapped (with a now-inaccurate "JSON marshal" message) as "failed to JSON marshal config linter settings". This error is propagated up as error 117's cause.
Source
Thrown at pkg/commands/run.go:688
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return nil, err
}
return h.Sum(nil), nil
}
// computeConfigSalt computes configuration hash.
// We don't hash all config fields to reduce meaningless cache invalidations.
// At least, it has a huge impact on tests speed.
// Fields: `LintersSettings` and `Run.BuildTags`.
func computeConfigSalt(cfg *config.Config) ([]byte, error) {
lintersSettingsBytes, err := yaml.Marshal(cfg.Linters.Settings)
if err != nil {
return nil, fmt.Errorf("failed to JSON marshal config linter settings: %w", err)
}
configData := bytes.NewBufferString("linters.settings=")
configData.Write(lintersSettingsBytes)
configData.WriteString("\nbuild-tags=%s" + strings.Join(cfg.Run.BuildTags, ","))
h := sha256.New()
if _, err := h.Write(configData.Bytes()); err != nil {
return nil, err
}
return h.Sum(nil), nil
}
func computeGoModSalt() (string, error) {
values, err := goenv.Get(context.Background(), goenv.GOMOD)
if err != nil {
return "", fmt.Errorf("failed to get goenv: %w", err)View on GitHub (pinned to ed7a235d2d)
Solutions
- Find what mutates cfg.Linters.Settings (plugins, custom builds, wrappers) and ensure only standard config types are stored there.
- Use an unmodified golangci-lint release binary to rule out patched-build issues.
- Simplify .golangci.yml linter settings and re-run to confirm stock configs marshal fine.
- Upgrade golangci-lint; the misleading 'JSON' wording was fixed in later versions.
Example fix
// before (custom code)
cfg.Linters.Settings.Custom["x"] = someOpaqueValue
// after
cfg.Linters.Settings.Custom["x"] = config.CustomLinterSettings{Path: "./x.so"} Defensive patterns
Strategy: try-catch
Validate before calling
// stock binary + stock config should always marshal
if _, err := yaml.Marshal(lintersSettingsStruct); err != nil {
return fmt.Errorf("settings not serializable; custom mutation detected: %w", err)
} Try / catch
if err := cmd.Run(); err != nil && strings.Contains(err.Error(), "failed to JSON marshal config linter settings") {
log.Fatalf("non-serializable value injected into Linters.Settings (plugin/custom build?): %v", err)
} Prevention
- Use official golangci-lint release binaries.
- Never mutate cfg.Linters.Settings programmatically with non-standard types.
- Test custom plugins against the exact golangci-lint version.
When it happens
Trigger: yaml.Marshal returns an error for the linter settings — practically only when the settings struct contains values the marshaller cannot represent, e.g. after programmatic mutation of the config in custom code or unsupported types injected by plugins.
Common situations: Forked/patched golangci-lint builds or custom formatters mutating Linters.Settings with non-serializable values; ABI/plugin mismatches where a custom linter writes incompatible data into settings.
Related errors
- failed to calculate config salt: %w
- can't read viper config: %w
- the configuration contains invalid elements
- unsupported configuration format
- the configuration contains invalid elements
AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02).
Data as JSON: /api/errors/ce13bf7cd3821cf4.
Report an issue: GitHub.