kubernetes/kops · error

path: %s already exists but is not a directory

Error message

path: %s already exists but is not a directory

What it means

NodeupModelContext.EnsureDirectory (nodeup/pkg/model/context.go:173) creates a directory with os.MkdirAll if missing, and otherwise verifies the existing path is a directory via os.Stat. This error means the path already exists on the node filesystem but is a regular file, symlink-to-file, socket, etc. — not a directory — so nodeup cannot use it as a directory. It is a filesystem state conflict, not a permission problem.

Source

Thrown at nodeup/pkg/model/context.go:173

		name += ".service"
	}

	return name
}

// EnsureDirectory ensures the directory exists or creates it
func (c *NodeupModelContext) EnsureDirectory(path string) error {
	st, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return os.MkdirAll(path, 0o755)
		}

		return err
	}

	if !st.IsDir() {
		return fmt.Errorf("path: %s already exists but is not a directory", path)
	}

	return nil
}

// IsMounted checks if the device is mount
func (c *NodeupModelContext) IsMounted(m mount.Interface, device, path string) (bool, error) {
	list, err := m.List()
	if err != nil {
		return false, err
	}

	for _, x := range list {
		if x.Device == device {
			klog.V(3).Infof("Found mountpoint device: %s, path: %s, type: %s", x.Device, x.Path, x.Type)
			if strings.TrimSuffix(x.Path, "/") == strings.TrimSuffix(path, "/") {
				return true, nil
			}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. SSH to the node and inspect the path: ls -la <path> to confirm it is a file
  2. Move the offending file aside (mv <path> <path>.bak) and re-run nodeup so MkdirAll creates the directory
  3. If the file is from a bad volume mount or image layer, fix the mount/image definition rather than just deleting the file
  4. After remediation, confirm the directory exists with correct 0755 permissions and restart the nodeup/bootstrap process

Example fix

# before: a file blocks the expected directory
$ ls -la /var/log/kops
-rw-r--r-- 1 root root 0 ... /var/log/kops
# after
$ mv /var/log/kops /var/log/kops.bak && mkdir -p /var/log/kops
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check on the node before bootstrapping:
st, err := os.Stat(path)
if err == nil && !st.IsDir() {
    return fmt.Errorf("%s must be a directory; move it aside first", path)
}

Try / catch

if err := ctx.EnsureDirectory(path); err != nil {
    if strings.Contains(err.Error(), "already exists but is not a directory") {
        // remediate: mv the file aside, then retry EnsureDirectory
        os.Rename(path, path+".bak")
        return ctx.EnsureDirectory(path)
    }
    return err
}

Prevention

When it happens

Trigger: Any Build step that calls EnsureDirectory(path) where os.Stat(path) succeeds, IsNotExist is false, and st.IsDir() is false — i.e. a file occupies a path nodeup expects to be a directory (e.g. /var/log/kops, mounts dirs, /etc/kubernetes manifests directories).

Common situations: A previous failed bootstrap left a regular file where a directory belongs; container/image builds or custom base images placed a file at a kops-managed path; a volume mount incorrectly mounted a file at a directory path; manual experimentation on the node created stray files.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/b12f96fb72095a6f. Report an issue: GitHub.