golangci/golangci-lint · error

open source file: %w

Error message

open source file: %w

What it means

copyBinary wraps the error from os.Open of the built binary inside the repo directory (filepath.Join(b.repo, binaryName), cleaned via filepath.Clean). The %w preserves the underlying *os.PathError (e.g. ENOENT, EACCES). It means the builder could not open the compiled artifact for reading before copying it to the destination.

Source

Thrown at pkg/commands/internal/builder.go:212

	)
	cmd.Dir = b.repo

	output, err := cmd.CombinedOutput()
	if err != nil {
		b.log.Warnf("%s", string(output))

		return fmt.Errorf("%s: %w", strings.Join(cmd.Args, " "), err)
	}

	return nil
}

func (b Builder) copyBinary(binaryName string) error {
	src := filepath.Join(b.repo, binaryName)

	source, err := os.Open(filepath.Clean(src))
	if err != nil {
		return fmt.Errorf("open source file: %w", err)
	}

	defer func() { _ = source.Close() }()

	info, err := source.Stat()
	if err != nil {
		return fmt.Errorf("stat source file: %w", err)
	}

	if b.cfg.Destination != "" {
		err = os.MkdirAll(b.cfg.Destination, os.ModePerm)
		if err != nil {
			return fmt.Errorf("create destination directory: %w", err)
		}
	}

	dst, err := os.OpenFile(filepath.Join(b.cfg.Destination, binaryName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
	if err != nil {

View on GitHub (pinned to ed7a235d2d)

Solutions

  1. Verify the built binary exists at filepath.Join(b.repo, binaryName) — run ls on that exact path after the build step
  2. Confirm getBinaryName() returns the same filename go build was told to produce (-o flag), including any GOOS/GOARCH prefixes
  3. Check the b.repo configuration points at the directory containing the artifact
  4. Check file permissions on the binary (chmod +r) and that the user running the build can read it
  5. Inspect the wrapped *os.PathError in the message to distinguish not-found (ENOENT) from permission (EACCES)

Example fix

// before
go build ./cmd/tool
// after
go build -o tool ./cmd/tool  # ensure output name matches binaryName used by copyBinary
Defensive patterns

Strategy: validation

Validate before calling

src := filepath.Join(repo, binaryName)
if info, err := os.Stat(src); err != nil {
    return fmt.Errorf("binary %s missing before copy: %w", src, err)
} else if info.IsDir() {
    return fmt.Errorf("%s is a directory, expected a binary", src)
}

Try / catch

if err := b.copyBinary(name); err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) && errors.Is(perr, fs.ErrNotExist) {
        log.Fatalf("built binary not found at %s: rebuild with the correct -o output name", perr.Path)
    }
    return err
}

Prevention

When it happens

Trigger: Builder.Build ran go build successfully (or skipped it) but os.Open(b.repo/<binaryName>) failed: the binary was not produced at the expected path, binaryName from getBinaryName() does not match the actual output file, the file was deleted between build and copy, or the process lacks read permission.

Common situations: go build output name differs from getBinaryName() (e.g. GOOS/GOARCH suffix added by -o or goreleaser-style naming), building in a dirty CI checkout where the artifact is elsewhere, repo path misconfigured so src points at a nonexistent directory, or read permissions stripped on the artifact.

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/5c45259f478a65ad. Report an issue: GitHub.