{"record":{"id":"cdc89820b45d6b6b","repo":"golangci/golangci-lint","slug":"can-t-read-from-patch-file-s-w","errorCode":null,"errorMessage":"can't read from patch file %s: %w","messagePattern":"can't read from patch file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/result/processors/diff.go","lineNumber":61,"sourceCode":"\t\tpatch:         os.Getenv(envGolangciDiffProcessorPatch),\n\t}\n}\n\nfunc (*Diff) Name() string {\n\treturn \"diff\"\n}\n\nfunc (p *Diff) Process(issues []*result.Issue) ([]*result.Issue, error) {\n\tif !p.onlyNew && p.fromRev == \"\" && p.fromMergeBase == \"\" && p.patchFilePath == \"\" && p.patch == \"\" {\n\t\treturn issues, nil\n\t}\n\n\tvar patchReader io.Reader\n\tswitch {\n\tcase p.patchFilePath != \"\":\n\t\tpatch, err := os.ReadFile(p.patchFilePath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't read from patch file %s: %w\", p.patchFilePath, err)\n\t\t}\n\n\t\tpatchReader = bytes.NewReader(patch)\n\n\tcase p.patch != \"\":\n\t\tpatchReader = strings.NewReader(p.patch)\n\t}\n\n\tchecker := revgrep.Checker{\n\t\tPatch:        patchReader,\n\t\tRevisionFrom: p.fromRev,\n\t\tMergeBase:    p.fromMergeBase,\n\t\tWholeFiles:   p.wholeFiles,\n\t}\n\n\terr := checker.Prepare(context.Background())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't prepare diff by revgrep: %w\", err)","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/golangci/golangci-lint/blob/ed7a235d2d771152056fdc142a9855567c5796a9/pkg/result/processors/diff.go#L43-L79","documentation":"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.","triggerScenarios":"Process is called with ProcessorOption patchFilePath set to a path that does not exist, is a directory, or is unreadable (permissions).","commonSituations":"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.","solutions":["Verify the patch file path exists and is spelled correctly","Ensure the patch file is created before golangci-lint runs (e.g. git diff > patch.txt in an earlier CI step)","Check file permissions / that the file is mounted into the container","Alternatively pass the patch inline via --patch instead of a file"],"exampleFix":"// before\np := diff.NewPatchProcessor(diff.WithPatchFilePath(\"./patches/current.diff\"))\n// after\nif _, err := os.Stat(\"./patches/current.diff\"); err != nil { log.Fatal(err) }\np := diff.NewPatchProcessor(diff.WithPatchFilePath(\"./patches/current.diff\"))","handlingStrategy":"validation","validationCode":"// Go: stat the patch file before constructing the processor\nif _, err := os.Stat(patchPath); err != nil {\n    return fmt.Errorf(\"patch file missing: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"issues, err := p.Process(issues)\nif err != nil {\n    var pe *os.PathError\n    if errors.As(err, &pe) { log.Fatalf(\"patch input problem: %v\", err) }\n    return err\n}","preventionTips":["Generate the patch file in an earlier, guaranteed CI step","Pass the patch inline via --patch when the file lifecycle is hard to control","Mount patch files explicitly in containers and verify with ls in CI"],"tags":["file-io","patch","golangci-lint"],"backgroundTag":"file-not-found","analyzedSha":"ed7a235d2d771152056fdc142a9855567c5796a9","analyzedAt":"2026-09-02T18:47:33.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}