juanfont/headscale · error

creating directory %s: %w

Error message

creating directory %s: %w

What it means

Returned by util.EnsureDir when os.MkdirAll fails for any reason other than a permission error (wrapped with the directory path and the raw error). Typical causes include a path component being a regular file, I/O errors, or invalid path syntax.

Source

Thrown at hscontrol/util/file.go:59

	modeStr := viper.GetString(key)

	mode, err := strconv.ParseUint(modeStr, Base8, BitSize64)
	if err != nil {
		return PermissionFallback
	}

	return fs.FileMode(mode) //nolint:gosec // file mode is bounded by ParseUint
}

func EnsureDir(dir string) error {
	if _, err := os.Stat(dir); os.IsNotExist(err) { //nolint:noinlineerr
		err := os.MkdirAll(dir, PermissionFallback)
		if err != nil {
			if errors.Is(err, os.ErrPermission) {
				return fmt.Errorf("%w: %s", ErrDirectoryPermission, dir)
			}

			return fmt.Errorf("creating directory %s: %w", dir, err)
		}
	}

	return nil
}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Check whether any component of the path already exists as a file and remove or relocate it
  2. Free disk space or remount the volume read-write if the error is EROFS/ENOSPC
  3. Inspect the wrapped error text for the syscall reason (ENOTDIR, ENOSPC, EROFS) and fix the underlying filesystem state

Example fix

# before
# /etc/headscale/certs is a regular file left by an old install
# after
sudo rm /etc/headscale/certs && sudo mkdir -p /etc/headscale/certs
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(dir); err == nil && !fi.IsDir() {
    return fmt.Errorf("path %s exists and is not a directory", dir)
}

Try / catch

if err := util.EnsureDir(dir); err != nil {
    if errors.Is(err, util.ErrDirectoryPermission) {
        // ownership fix path
    }
    // inspect wrapped syscall error: ENOTDIR => collision with a file, ENOSPC => disk
    return err
}

Prevention

When it happens

Trigger: Calling EnsureDir where some component of dir already exists as a file (e.g. /var/lib/headscale is a file), extremely long paths, or ENOSPC/EROFS device errors during MkdirAll.

Common situations: A config pointing tls_cert_path or state storage under a path that collides with an existing file; disk-full conditions in containers; leftover artifacts from previous installations.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/2fd4410762da627e. Report an issue: GitHub.