golangci/golangci-lint

"panic: close of closed channel" in v2.2.1

Closed this issue · 4 comments

Welcome

  • Yes, I'm using a binary release within 2 latest releases. Only such installations are supported.
  • Yes, I've searched similar issues on GitHub and didn't find any.
  • Yes, I've read the typecheck section of the FAQ.
  • Yes, I've tried with the standalone linter if available (e.g., gocritic, go vet, etc.).
  • I agree to follow this project's Code of Conduct

How did you install golangci-lint?

Nix

Description of the problem

golangci-lint v2.2.1 just panicked for me on my https://github.com/leonklingele/lmk repo. I couldn't reproduce the panic, so it's probably a data race.

Steps to reproduce, including the config files & specific version of the binaries used:

$ git clone --depth 1 https://github.com/leonklingele/lmk
$ cd lmk
$ git checkout d2e28797d45b1c7a290dd3079c054a2568b74f37
$ nix-shell # or direnv allow

# Apply the following patch:
diff --git a/main.go b/main.go
index 1e99350ac0db92349d3c565428dee2cc4187f170..5116a696ada57f032c80a6ef3c8e6ba54c7bd11a 100644
--- a/main.go
+++ b/main.go
@@ -427 +427 @@ func main() {
-    ctx := context.Background()
+    ctx = context.Background()

$ OUT="" ; until [[ "$OUT" == *"panic"* ]]; do OUT="$(make lint 2>&1)" ; done ; echo "$OUT"
INFO golangci-lint has version 2.2.1 built with go1.24.4 from v2.2.1 on 19700101-00:00:00
INFO [config_reader] Config search paths: [./ /Users/leon/code/lmk /Users/leon/code /Users/leon /Users /]
INFO [config_reader] Used config file .golangci.yml
INFO [config_reader] Module name "github.com/leonklingele/lmk"
INFO maxprocs: Leaving GOMAXPROCS=14: CPU quota undefined
INFO [goenv] Read go env for 4.600167ms: map[string]string{"GOCACHE":"/Users/leon/code/lmk/.gopath/.cache", "GOROOT":"/nix/store/rq7irijkj3nhapmjcv9d96xgkisj55x2-go-1.24.4/share/go"}
INFO [lintersdb] Active 89 linters: [asasalint asciicheck bidichk bodyclose canonicalheader containedctx contextcheck copyloopvar decorder depguard dogsled dupword durationcheck embeddedstructfieldcheck err113 errcheck errchkjson errname errorlint exhaustive exptostd fatcontext forbidigo forcetypeassert funcorder gci ginkgolinter gocheckcompilerdirectives gochecknoglobals gochecknoinits gochecksumtype goconst gocritic gofmt gofumpt goimports gomoddirectives goprintffuncname gosec gosmopolitan govet grouper iface ineffassign intrange loggercheck makezero mirror misspell mnd musttag nakedret nestif nilerr nilnesserr nilnil noctx nolintlint nonamedreturns nosprintfhostport paralleltest perfsprint predeclared promlinter protogetter reassign recvcheck revive rowserrcheck sloglint spancheck sqlclosecheck staticcheck tagalign tagliatelle testableexamples testifylint testpackage thelper tparallel unconvert unparam unused usestdlibvars usetesting wastedassign whitespace wrapcheck zerologlint]
INFO [loader] Go packages loading at mode 8767 (types_sizes|exports_file|imports|name|compiled_files|deps|files) took 192.021375ms
INFO [runner/filename_unadjuster] Pre-built 0 adjustments in 371.959µs
panic: close of closed channel

goroutine 5508 [running]:
github.com/golangci/golangci-lint/v2/pkg/goanalysis.(*loadingPackage).analyze.func2(0x14003e2da80)
	github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go:109 +0x11c
created by github.com/golangci/golangci-lint/v2/pkg/goanalysis.(*loadingPackage).analyze in goroutine 786
	github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go:91 +0x174
make: *** [Makefile:14: lint] Error 2

Version of golangci-lint

$ golangci-lint --version
golangci-lint has version 2.2.1 built with go1.24.4 from v2.2.1 on 19700101-00:00:00

Configuration

See https://github.com/leonklingele/lmk/blob/d2e28797d45b1c7a290dd3079c054a2568b74f37/.golangci.yml

Go environment

$ go version && go env
# paste output here

Verbose output of running

$ golangci-lint cache clean
$ golangci-lint run -v
# See above, not reproducible

A minimal reproducible example or link to a public repository

See above

Validation

  • Yes, I've included all information above (version, config, etc.).

Supporter

ldez commented

I don't reproduce your issue:

$ docker run -it --rm golangci/golangci-lint:v2.2.1 bash
root@8b6c95db622b:/go# cd /tmp/
root@8b6c95db622b:/tmp# git clone --depth 1 https://github.com/leonklingele/lmk
Cloning into 'lmk'...
remote: Enumerating objects: 23, done.
remote: Counting objects: 100% (23/23), done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 23 (delta 3), reused 9 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (23/23), 17.10 KiB | 1.14 MiB/s, done.
Resolving deltas: 100% (3/3), done.
root@8b6c95db622b:/tmp# cd lmk
root@8b6c95db622b:/tmp/lmk# git checkout d2e28797d45b1c7a290dd3079c054a2568b74f37
Note: switching to 'd2e28797d45b1c7a290dd3079c054a2568b74f37'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at d2e2879 chore(deps): update to golangci-lint v2.2.1
root@8b6c95db622b:/tmp/lmk# make lint
INFO golangci-lint has version 2.2.1 built with go1.24.4 from 66496a99 on 2025-06-29T21:03:24Z 
INFO [config_reader] Config search paths: [./ /tmp/lmk /tmp / /root] 
INFO [config_reader] Used config file .golangci.yml 
INFO [config_reader] Module name "github.com/leonklingele/lmk" 
INFO maxprocs: Leaving GOMAXPROCS=16: CPU quota undefined 
INFO [goenv] Read go env for 1.752951ms: map[string]string{"GOCACHE":"/root/.cache/go-build", "GOROOT":"/usr/local/go"} 
INFO [lintersdb] Active 89 linters: [asasalint asciicheck bidichk bodyclose canonicalheader containedctx contextcheck copyloopvar decorder depguard dogsled dupword durationcheck embeddedstructfieldcheck err113 errcheck errchkjson errname errorlint exhaustive exptostd fatcontext forbidigo forcetypeassert funcorder gci ginkgolinter gocheckcompilerdirectives gochecknoglobals gochecknoinits gochecksumtype goconst gocritic gofmt gofumpt goimports gomoddirectives goprintffuncname gosec gosmopolitan govet grouper iface ineffassign intrange loggercheck makezero mirror misspell mnd musttag nakedret nestif nilerr nilnesserr nilnil noctx nolintlint nonamedreturns nosprintfhostport paralleltest perfsprint predeclared promlinter protogetter reassign recvcheck revive rowserrcheck sloglint spancheck sqlclosecheck staticcheck tagalign tagliatelle testableexamples testifylint testpackage thelper tparallel unconvert unparam unused usestdlibvars usetesting wastedassign whitespace wrapcheck zerologlint] 
INFO [loader] Go packages loading at mode 8767 (name|types_sizes|deps|exports_file|imports|compiled_files|files) took 15.834287425s 
INFO [runner/filename_unadjuster] Pre-built 0 adjustments in 459.609µs 
INFO [linters_context/goanalysis] analyzers took 16.369710845s with top 10 stages: buildir: 8.115653136s, buildssa: 3.214162676s, exhaustive: 1.603072266s, revive: 755.006202ms, inspect: 666.170841ms, gocritic: 464.375069ms, findcall: 323.330489ms, printf: 264.092184ms, ctrlflow: 202.095836ms, fact_deprecated: 166.580928ms 
INFO [runner/exclusion_rules] Skipped 2 issues by rules: [Text: "(?i)do not define dynamic errors, use wrapped static errors instead*", Linters: "err113"] 
INFO [runner] Issues before processing: 19, after processing: 0 
INFO [runner] Processors filtering stat (in/out): filename_unadjuster: 19/19, path_relativity: 19/19, path_absoluter: 19/19, exclusion_paths: 19/19, nolint_filter: 17/0, cgo: 19/19, invalid_issue: 19/19, generated_file_filter: 19/19, exclusion_rules: 19/17 
INFO [runner] processing took 682.435µs with stages: nolint_filter: 599.967µs, exclusion_rules: 43.794µs, generated_file_filter: 26.215µs, path_relativity: 5.297µs, filename_unadjuster: 1.2µs, sort_results: 987ns, cgo: 955ns, invalid_issue: 835ns, max_same_issues: 650ns, severity-rules: 505ns, fixer: 387ns, exclusion_paths: 352ns, path_absoluter: 343ns, diff: 185ns, path_prettifier: 167ns, uniq_by_line: 143ns, path_shortener: 117ns, max_per_file_from_linter: 116ns, max_from_linter: 113ns, source_code: 107ns 
INFO [runner] linters took 6.548276216s with stages: goanalysis_metalinter: 6.547544848s 
0 issues.
INFO File cache stats: 0 entries of total size 0B 
INFO Memory: 225 samples, avg is 280.1MB, max is 1274.0MB 
INFO Execution took 22.387039585s                 
root@8b6c95db622b:/tmp/lmk# 

The chan is closed only if there are typecheck errors, in your context (your commit), the channel is never closed.
So the logs are not related to this specific commit.

So the logs are not related to this specific commit.

Thanks for pointing this out, there might have been some local changes (sorry!). After further investigation, I was able to reproduce! 🥳

You need to apply this patch (or any other change to produce a typecheck error):

diff --git a/main.go b/main.go
index 1e99350ac0db92349d3c565428dee2cc4187f170..5116a696ada57f032c80a6ef3c8e6ba54c7bd11a 100644
--- a/main.go
+++ b/main.go
@@ -427 +427 @@ func main() {
-    ctx := context.Background()
+    ctx = context.Background()

Then just repeat the make lint until a panic occurs:

OUT="" ; until [[ "$OUT" == *"panic"* ]]; do OUT="$(make lint 2>&1)" ; done ; echo "$OUT"

I'm able to reproduce the issue locally:

$ mkdir empty
$ cd empty
$ golangci-lint run
ERRO [linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies 
0 issues.
$ golangci-lint run
panic: close of closed channel

goroutine 200 [running]:
github.com/golangci/golangci-lint/v2/pkg/goanalysis.(*loadingPackage).analyze.func2(0x14001b108c8)
        github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go:109 +0x11c
created by github.com/golangci/golangci-lint/v2/pkg/goanalysis.(*loadingPackage).analyze in goroutine 175
        github.com/golangci/golangci-lint/v2/pkg/goanalysis/runner_loadingpackage.go:91 +0x174
Go env

$ go env
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/alexandear/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/alexandear/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/q0/5_z6pvw574z1c0zcrr2xvk0r0000gn/T/go-build3063022420=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/Users/alexandear/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/alexandear/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.24.4/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/alexandear/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.24.4/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.4'
GOWORK=''
PKG_CONFIG='pkg-config'

ldez commented

I'm assigned to this issue