golangci/golangci-lint · error

%s: %w

Error message

%s: %w

What it means

runLinterSafe wraps linter execution in a recover() defer. If a linter panics with an *errorutil.PanicError, it is converted to "<lintername>: <panic error>" and logged with its stack trace; other panic values become a generic "panic occurred" error. This prevents one linter's crash from aborting the whole golangci-lint run.

Source

Thrown at pkg/lint/runner.go:162

			lintErrors = errors.Join(lintErrors, fmt.Errorf("can't run linter %s", lc.Linter.Name()), err)
			r.Log.Warnf("Can't run linter %s: %v", lc.Linter.Name(), err)

			continue
		}

		issues = append(issues, linterIssues...)
	}

	return r.processLintResults(issues), lintErrors
}

func (r *Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context,
	lc *linter.Config,
) (ret []*result.Issue, err error) {
	defer func() {
		if panicData := recover(); panicData != nil {
			if pe, ok := panicData.(*errorutil.PanicError); ok {
				err = fmt.Errorf("%s: %w", lc.Name(), pe)

				// Don't print stacktrace from goroutines twice
				r.Log.Errorf("Panic: %s: %s", pe, pe.Stack())
			} else {
				err = fmt.Errorf("panic occurred: %s", panicData)
				r.Log.Errorf("Panic stack trace: %s", debug.Stack())
			}
		}
	}()

	issues, err := lc.Linter.Run(ctx, lintCtx)

	if lc.DoesChangeTypes {
		// Packages in lintCtx might be dirty due to the last analysis,
		// which affects to the next analysis.
		// To avoid this issue, we clear type information from the packages.
		// See https://github.com/golangci/golangci-lint/pull/944.
		// Currently, DoesChangeTypes is true only for `unused`.

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Identify the linter from the message prefix and check its issue tracker for known panics on your code
  2. Upgrade golangci-lint (and bundled linters) to the latest patch release where the panic may be fixed
  3. Exclude the triggering file/pattern via `linters.exclusions.rules` or `linters.exclusions.paths` as a workaround
  4. Reduce to a minimal reproducing file and report the panic upstream with the logged stack trace

Example fix

// before (.golangci.yml)
linters:
  enable:
    - buggedlinter   # panics on generated code
// after
linters:
  enable:
    - buggedlinter
  exclusions:
    rules:
      - path: '.*_gen\.go'
        linters:
          - buggedlinter
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-flight: run the linter standalone on a small sample to detect panicking versions:
// golangci-lint run --enable-only <name> ./... || echo "linter panics; pin or exclude"

Try / catch

defer func() {
    if p := recover(); p != nil {
        if pe, ok := p.(*errorutil.PanicError); ok {
            err = fmt.Errorf("%s: %w", lc.Name(), pe)
        } else {
            err = fmt.Errorf("panic occurred: %s", p)
        }
    }
}()

Prevention

When it happens

Trigger: A bug inside a linter (index out of range, nil dereference) triggered while analyzing specific code patterns, concurrent map access, or a linter analysis pass hitting an unexpected AST/type state. The panic is recovered in runLinterSafe and surfaced as this wrapped error.

Common situations: Linter bugs on unusual code (generics edge cases, exotic build tags); incompatible linter plugin versions; known upstream bugs in a linter release that are fixed in a later version.

Related errors


AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02). Data as JSON: /api/errors/bf0dc4d7026b56a8. Report an issue: GitHub.