golangci/golangci-lint · error
unable to open file %s: %w
Error message
unable to open file %s: %w
What it means
When a formatter runs as an analyzer pass, golangci-lint reads the target file from disk before passing it to the formatter. If os.ReadFile fails, the filename and cause are wrapped in this error and the pass fails. This means the file listed in the diagnostics/positions no longer exists or is unreadable.
Source
Thrown at pkg/goformatters/analyzer.go:31
"github.com/golangci/golangci-lint/v2/pkg/goformatters/internal"
"github.com/golangci/golangci-lint/v2/pkg/logutils"
)
// NewAnalyzer converts a [Formatter] to an [analysis.Analyzer].
func NewAnalyzer(logger logutils.Log, doc string, formatter Formatter) *analysis.Analyzer {
return &analysis.Analyzer{
Name: formatter.Name(),
Doc: doc,
Run: func(pass *analysis.Pass) (any, error) {
for _, file := range pass.Files {
position, isGoFile := goanalysis.GetGoFilePosition(pass, file)
if !isGoFile {
continue
}
input, err := os.ReadFile(position.Filename)
if err != nil {
return nil, fmt.Errorf("unable to open file %s: %w", position.Filename, err)
}
output, err := formatter.Format(position.Filename, input)
if err != nil {
return nil, fmt.Errorf("error while running %s: %w", formatter.Name(), err)
}
if !bytes.Equal(input, output) {
newName := filepath.ToSlash(position.Filename)
oldName := newName + ".orig"
patch := diff.Diff(oldName, input, newName, output)
err = internal.ExtractDiagnosticFromPatch(pass, file, patch, logger)
if err != nil {
return nil, fmt.Errorf("can't extract issues from %s diff output %q: %w", formatter.Name(), patch, err)
}
}View on GitHub (pinned to ed7a235d2d)
Solutions
- Regenerate/restore the missing file, or re-run `go generate ./...`
- Check file permissions and ownership (chmod/chown)
- Re-run the linter from a clean checkout (git status; discard stale artifacts)
- Ensure no concurrent process deletes/renames files during the lint
Example fix
// shell: restore deleted generated file before linting // before: golangci-lint fmt (file missing) // after go generate ./... && golangci-lint fmt
Defensive patterns
Strategy: validation
Validate before calling
// check file readability before running formatters
if _, err := os.Stat(path); err != nil {
log.Fatalf("file missing/unreadable: %v", err)
} Try / catch
// in the analyzer: wrap and skip gracefully
if _, err := os.ReadFile(path); err != nil {
return fmt.Errorf("unable to open file %s: %w", path, err)
} Prevention
- Regenerate files before linting (go generate)
- Never delete/move files concurrently with lint
- Fix permissions on generated artifacts
When it happens
Trigger: position.Filename points to a file that was deleted, renamed, or is unreadable (permissions) at the time the formatter analyzer runs.
Common situations: Generated files removed between listing and formatting; files deleted by a prior run without --fix cleanup; permission issues (running as different user, root-owned files); files on unmounted volumes.
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
- failed to create meta-formatter: %w
- build walk options: %w
- failed to process files: %w
- can't get enabled formatters: %w
- reading go.mod file: %w
AI-assisted analysis of golangci/golangci-lint@ed7a235d2d (2026-09-02).
Data as JSON: /api/errors/c9c6fb841f890f9f.
Report an issue: GitHub.