golangci/golangci-lint · error

can't read from patch file %s: %w

Error message

can't read from patch file %s: %w

What it means

The diff processor cannot read the patch file given via the --patch or patch-file option. os.ReadFile fails and the error is wrapped with the offending file path so the developer knows which patch input is broken. Without a patch, golangci-lint cannot compute which lines changed, so processing cannot continue.

Source

Thrown at pkg/result/processors/diff.go:61

		patch:         os.Getenv(envGolangciDiffProcessorPatch),
	}
}

func (*Diff) Name() string {
	return "diff"
}

func (p *Diff) Process(issues []*result.Issue) ([]*result.Issue, error) {
	if !p.onlyNew && p.fromRev == "" && p.fromMergeBase == "" && p.patchFilePath == "" && p.patch == "" {
		return issues, nil
	}

	var patchReader io.Reader
	switch {
	case p.patchFilePath != "":
		patch, err := os.ReadFile(p.patchFilePath)
		if err != nil {
			return nil, fmt.Errorf("can't read from patch file %s: %w", p.patchFilePath, err)
		}

		patchReader = bytes.NewReader(patch)

	case p.patch != "":
		patchReader = strings.NewReader(p.patch)
	}

	checker := revgrep.Checker{
		Patch:        patchReader,
		RevisionFrom: p.fromRev,
		MergeBase:    p.fromMergeBase,
		WholeFiles:   p.wholeFiles,
	}

	err := checker.Prepare(context.Background())
	if err != nil {
		return nil, fmt.Errorf("can't prepare diff by revgrep: %w", err)

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Verify the patch file path exists and is spelled correctly
  2. Ensure the patch file is created before golangci-lint runs (e.g. git diff > patch.txt in an earlier CI step)
  3. Check file permissions / that the file is mounted into the container
  4. Alternatively pass the patch inline via --patch instead of a file

Example fix

// before
p := diff.NewPatchProcessor(diff.WithPatchFilePath("./patches/current.diff"))
// after
if _, err := os.Stat("./patches/current.diff"); err != nil { log.Fatal(err) }
p := diff.NewPatchProcessor(diff.WithPatchFilePath("./patches/current.diff"))
Defensive patterns

Strategy: validation

Validate before calling

// Go: stat the patch file before constructing the processor
if _, err := os.Stat(patchPath); err != nil {
    return fmt.Errorf("patch file missing: %w", err)
}

Try / catch

issues, err := p.Process(issues)
if err != nil {
    var pe *os.PathError
    if errors.As(err, &pe) { log.Fatalf("patch input problem: %v", err) }
    return err
}

Prevention

When it happens

Trigger: Process is called with ProcessorOption patchFilePath set to a path that does not exist, is a directory, or is unreadable (permissions).

Common situations: Typoed path in --patch CI argument; patch file generated in a previous CI step that didn't run; running in a container where the patch was not mounted; permissions after downloading the patch as another user.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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