"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
typechecksection 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 2Version 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:00Configuration
See https://github.com/leonklingele/lmk/blob/d2e28797d45b1c7a290dd3079c054a2568b74f37/.golangci.ymlGo environment
$ go version && go env
# paste output hereVerbose output of running
$ golangci-lint cache clean
$ golangci-lint run -v
# See above, not reproducibleA minimal reproducible example or link to a public repository
See above
Validation
- Yes, I've included all information above (version, config, etc.).
Supporter
- I am a sponsor/backer through GitHub or OpenCollective
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 +0x174Go 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'I'm assigned to this issue