ipfs/kubo · warning

opening binary %q: %w

Error message

opening binary %q: %w

What it means

After resolving the path (via /proc/<pid>/exe on Linux or os.Executable elsewhere), binary() opens the executable with os.Open(path) to copy it into the archive. This error means the resolved binary path exists as a concept but could not be opened for reading, wrapped with the actual path for diagnosis.

Source

Thrown at profile/profile.go:237

}

func binary(ctx context.Context, _ Options, w io.Writer) error {
	var (
		path string
		err  error
	)
	if goos == "linux" {
		pid := os.Getpid()
		path = fmt.Sprintf("/proc/%d/exe", pid)
	} else {
		path, err = os.Executable()
		if err != nil {
			return fmt.Errorf("finding binary path: %w", err)
		}
	}
	fi, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("opening binary %q: %w", path, err)
	}
	_, err = io.Copy(w, fi)
	_ = fi.Close()
	if err != nil {
		return fmt.Errorf("copying binary %q: %w", path, err)
	}
	return nil
}

func mutexProfile(ctx context.Context, opts Options, w io.Writer) error {
	prev := runtime.SetMutexProfileFraction(opts.MutexProfileFraction)
	defer runtime.SetMutexProfileFraction(prev)
	err := waitOrCancel(ctx, opts.ProfileDuration)
	if err != nil {
		return err
	}
	return pprof.Lookup("mutex").WriteTo(w, 2)
}

View on GitHub (pinned to 329838acdf)

Solutions

  1. Check the wrapped path and verify it exists and is readable by the process user (ls -l, test -r)
  2. Relax SELinux/AppArmor rules or run the diagnostics collection as a user that can read the binary
  3. Keep the executable file present while the process runs (do not delete-on-upgrade before profiling)
  4. Skip the binary-embedding collector gracefully if unreadable — treat it as optional in your profile set

Example fix

// before
err := WriteProfiles(ctx, p) // fails hard: opening binary "/app/ipfs": permission denied
// after
if _, serr := os.Stat(binPath); serr == nil {
    if _, oerr := os.Open(binPath); oerr == nil {
        err := WriteProfiles(ctx, p)
    }
} // or drop the 'binary' collector from the profile list when unreadable
Defensive patterns

Strategy: validation

Validate before calling

path := binPathFor(runtime.GOOS) // /proc/self/exe or os.Executable()
if fi, err := os.Open(path); err != nil {
    log.Printf("binary %s unreadable, skip embedding: %v", path, err)
} else {
    fi.Close()
}

Try / catch

if err := WriteProfiles(ctx, p); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && strings.HasPrefix(err.Error(), "opening binary") {
        log.Printf("cannot read binary %s (%v); disable the binary collector", pe.Path, pe.Err)
    }
    return err
}

Prevention

When it happens

Trigger: os.Open(path) fails: the executable file was deleted after path resolution (dangling path on non-Linux), the process lacks read permission on the binary, /proc/<pid>/exe resolution failed on Linux (e.g. permission checks on deleted binaries), or a security module (SELinux/AppArmor) blocks reading the executable.

Common situations: Binaries run with dropped privileges that can no longer read their own image; hardened containers with noexec/no-read policies; deleted-but-running executables on non-Linux systems; SELinux enforcing profiles denying /proc/pid/exe reads.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/315f4821938f1747. Report an issue: GitHub.