ahmetb/kubectx · error

failed to open file %q: %w

Error message

failed to open file %q: %w

What it means

StandardKubeconfigLoader.Load opens each candidate kubeconfig path with os.OpenFile(..., os.O_RDWR, 0). Paths that don't exist are silently skipped, but any other open error (permissions, is-a-directory, device errors) aborts with this error wrapping the original *os.PathError, which names the failing path in %q and the syscall error in %w.

Source

Thrown at internal/kubeconfig/kubeconfigloader.go:52

	path string
}

func (kf *kubeconfigFile) Path() string { return kf.path }

func (*StandardKubeconfigLoader) Load() ([]ReadWriteResetCloser, error) {
	paths, err := kubeconfigPaths()
	if err != nil {
		return nil, fmt.Errorf("cannot determine kubeconfig path: %w", err)
	}

	var files []ReadWriteResetCloser
	for _, p := range paths {
		f, err := os.OpenFile(p, os.O_RDWR, 0)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return nil, fmt.Errorf("failed to open file %q: %w", p, err)
		}
		files = append(files, &kubeconfigFile{File: f, path: p})
	}
	if len(files) == 0 {
		return nil, fmt.Errorf("kubeconfig file not found: %w",
			&os.PathError{Op: "open", Path: paths[0], Err: os.ErrNotExist})
	}
	return files, nil
}

func (kf *kubeconfigFile) Reset() error {
	if err := kf.Truncate(0); err != nil {
		return fmt.Errorf("failed to truncate file: %w", err)
	}
	if _, err := kf.Seek(0, 0); err != nil {
		return fmt.Errorf("failed to seek in file: %w", err)
	}
	return nil

View on GitHub (pinned to 12ad6fb22e)

Solutions

  1. Read the wrapped *os.PathError in %w for the exact path and errno, then chmod/chown the file to be writable by the current user
  2. If the path should not be opened, unset it from KUBECONFIG: export KUBECONFIG="$HOME/.kube/config"
  3. If the path is a directory, point KUBECONFIG at actual files only (colon-separated)
  4. Verify the file is not on a read-only/failed mount (ls -l, mount, df)

Example fix

// before
export KUBECONFIG=/root/.kube/config   # permission denied for current user
// after
export KUBECONFIG=$HOME/.kube/config
Defensive patterns

Strategy: validation

Validate before calling

// Go
for _, p := range paths {
    if fi, err := os.Stat(p); err == nil {
        if fi.IsDir() { return fmt.Errorf("%s is a directory", p) }
        if f, err := os.OpenFile(p, os.O_RDWR, 0); err != nil {
            return fmt.Errorf("%s not openable read-write: %v", p, err)
        } else { f.Close() }
    }
}

Type guard

func isPermissionErr(err error) bool {
    return errors.Is(err, os.ErrPermission)
}

Try / catch

files, err := loader.Load()
var pe *fs.PathError
if errors.As(err, &pe) {
    return fmt.Errorf("cannot open %s: %v — check permissions", pe.Path, pe.Err)
}

Prevention

When it happens

Trigger: Calling Load when a path from $KUBECONFIG (or the default ~/.kube/config) exists but cannot be opened for reading and writing — e.g. permission denied (chmod 000 / root-owned file), the path is a directory, or an I/O error occurs.

Common situations: KUBECONFIG pointing at a file owned by another user or with 0600 root-only permissions; a stale KUBECONFIG entry that now names a directory; file locked on a network share; running the tool unprivileged against another user's config.

Related errors


AI-assisted analysis of ahmetb/kubectx@12ad6fb22e (2026-09-02). Data as JSON: /api/errors/88a99f6aedf22f9d. Report an issue: GitHub.