Skip to content

add golangci-lint with errorlint and wrapcheck to CI to prevent error-handling regressions #696

@Atishyy27

Description

@Atishyy27

Description

The repository has seen a steady stream of error-handling related issues and
fixes (#694, #691, #675, #677). Each of these requires a human reviewer to
catch violations manually during code review. Without automated enforcement,
the same classes of mistakes will recur as new contributors add code.

Adding golangci-lint to the CI pipeline — specifically with the errorlint
and wrapcheck linters enabled — would automatically catch:

  • errorlint: use of %v instead of %w in fmt.Errorf (breaking
    errors.Is/errors.As), comparison of errors with == instead of
    errors.Is, and type assertions on errors instead of errors.As
  • wrapcheck: errors from external packages returned unwrapped (loses
    context and origin)
  • staticcheck: broad set of correctness and simplification checks already
    standard across the Go ecosystem

This makes the project's error-handling expectations machine-checkable,
removing the burden from reviewers and giving contributors immediate feedback
on every PR.

Steps to Reproduce

The gap is visible from the CI configuration. Running the following against the
current codebase surfaces violations that would have been caught automatically:

# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# Run with errorlint enabled
golangci-lint run --enable errorlint ./...

# Run with wrapcheck enabled
golangci-lint run --enable wrapcheck ./...

The output will list locations where errors are wrapped with %v, compared
with ==, or returned unwrapped from external packages.

Expected Behavior

The CI pipeline rejects PRs that introduce error-handling violations.
Contributors get fast, automatic feedback rather than discovering issues
in code review after the fact.

Current Behavior

Error-handling conventions are enforced only through manual code review.
This is inconsistent, puts the burden on reviewers, and allows the same
issue classes to recur across PRs.

Proposed Solution

1. Add .golangci.yml at the repository root:

run:
  timeout: 5m

linters:
  disable-all: true
  enable:
    - errorlint
    - wrapcheck
    - govet
    - staticcheck

linters-settings:
  errorlint:
    errorf: true
    asserts: true
    comparison: true

  wrapcheck:
    ignoreSigs:
      - .Errorf(
      - errors.New(
      - errors.Unwrap(
      - .Wrap(
      - .Wrapf(

issues:
  exclude-rules:
    - path: _test\.go
      linters:
        - wrapcheck

2. Add a lint job to the workflow:

# .github/workflows/lint.yml
name: Lint

on:
  push:
    branches: [main]
  pull_request:

jobs:
  golangci:
    name: golangci-lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - uses: golangci/golangci-lint-action@v6
        with:
          version: latest
          args: --timeout=5m

3. Fix any violations the linter surfaces in a follow-up PR. Acceptable
exceptions can be annotated with //nolint:errorlint // reason inline.

This approach is incremental; the config can start conservative and tighten
as existing violations are cleaned up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions