golangci/golangci-lint · error

parallel golangci-lint is running

Error message

parallel golangci-lint is running

What it means

`runCommand.preRunE` (pkg/commands/run.go:217) acquires an exclusive file lock before running the linter. If another golangci-lint process already holds the lock (`c.acquireFileLock()` returns false), the new process aborts with this error. It prevents concurrent golangci-lint runs from clobbering the shared cache, which can corrupt analysis results. The lock is released by `postRun` when the previous run finishes.

Source

Thrown at pkg/commands/run.go:217

	sw := timeutils.NewStopwatch("pkgcache", c.log.Child(logutils.DebugKeyStopwatch))

	pkgCache, err := cache.NewCache(sw, c.log.Child(logutils.DebugKeyPkgCache))
	if err != nil {
		return fmt.Errorf("failed to build packages cache: %w", err)
	}

	guard := load.NewGuard()

	pkgLoader := lint.NewPackageLoader(c.log.Child(logutils.DebugKeyLoader), c.cfg, args, c.goenv, guard)

	c.contextBuilder = lint.NewContextBuilder(c.cfg, pkgLoader, pkgCache, guard)

	if err = initHashSalt(c.log.Child(logutils.DebugKeyGoModSalt), c.buildInfo.Version, c.cfg); err != nil {
		return fmt.Errorf("failed to init hash salt: %w", err)
	}

	if ok := c.acquireFileLock(); !ok {
		return errors.New("parallel golangci-lint is running")
	}

	return nil
}

func (c *runCommand) postRun(_ *cobra.Command, _ []string) {
	c.releaseFileLock()
}

func (c *runCommand) execute(_ *cobra.Command, _ []string) {
	needTrackResources := logutils.IsVerbose() || c.opts.PrintResourcesUsage

	trackResourcesEndCh := make(chan struct{})

	// Note: this defer must be before ctx.cancel defer
	defer func() {
		// wait until resource tracking finished to print properly
		if needTrackResources {

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Wait for the currently running golangci-lint process to finish, then re-run the command
  2. Find and stop the concurrent instance (editor plugin, watch mode, or parallel CI job) before linting
  3. Serialize linting in CI/scripts (e.g. a lock or `flock`) or configure the editor to not auto-run golangci-lint
  4. If a stale process is truly gone, remove leftover lock state from the cache directory and retry

Example fix

# before (Makefile)
check: lint test
lint:
	golangci-lint run &
	go test ./...
# after
check: lint test
lint:
	golangci-lint run
	go test ./...
Defensive patterns

Strategy: retry

Validate before calling

// wait for an in-flight run to release the lock before re-running
for attempts := 0; attempts < 30; attempts++ {
	if err := exec.Command("golangci-lint", "run").Run(); err == nil {
		break
	} else if !strings.Contains(err.Error(), "parallel golangci-lint is running") {
		break
	}
	time.Sleep(2 * time.Second)
}

Try / catch

out, err := cmd.CombinedOutput()
if err != nil && strings.Contains(string(out), "parallel golangci-lint is running") {
	time.Sleep(5 * time.Second) // then retry once
}

Prevention

When it happens

Trigger: Launching a second `golangci-lint run` in the same project while a previous instance is still executing; an editor plugin/IDE (e.g. gopls integration or language server) auto-running the linter in the background at the same time as a manual/CI run.

Common situations: Parallel CI jobs or make targets linting the same checkout concurrently; an IDE's on-save lint colliding with a terminal run; a previous golangci-lint run crashed leaving lock state that has not yet been cleaned up; running the linter on the same directory from two shells.

Related errors


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