cloudflare/cloudflared · error

create config dir at %s: %w

Error message

create config dir at %s: %w

What it means

ensureConfigDirExists creates the token configuration directory with os.Mkdir(0o755) before a tunnel token is written into it. Any mkdir error other than os.ErrExist is wrapped as 'create config dir at <path>: <cause>'. It usually reflects a filesystem permission problem or a bad path component (e.g. an existing regular file where the directory should be).

Source

Thrown at cmd/cloudflared/common_service.go:25

	"path/filepath"

	"github.com/rs/zerolog"
	"github.com/urfave/cli/v2"

	"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
	"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
)

const (
	defaultTokenFile = "token"
)

func ensureConfigDirExists(configDir string) error {
	if err := os.Mkdir(configDir, 0o755); err != nil { //nolint:gosec // config dir must be traversable by non-root user
		if errors.Is(err, os.ErrExist) {
			return nil
		}
		return fmt.Errorf("create config dir at %s: %w", configDir, err)
	}
	return nil
}

func createTokenFileUnix(path string) error {
	const tokenPerms os.FileMode = 0o600
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, tokenPerms) //nolint:gosec // All callers of this function construct path from constant strings or well-known env vars (e.g., $HOME)
	if err != nil {
		return fmt.Errorf("create token file at %s: %w", path, err)
	}
	defer func() { _ = f.Close() }()

	// If the file already existed with unrestrictive permissions, os.OpenFile
	// will not update its permissions, so perform an extra os.Chmod
	if err := os.Chmod(path, tokenPerms); err != nil {
		return fmt.Errorf("chmod token file at %s: %w", path, err)
	}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Check ownership/permissions of the config directory path and chown/chmod it appropriately
  2. Ensure the path is a directory: `ls -ld <configDir>`; remove or rename a conflicting file
  3. Run with elevated privileges if the location requires root, or set the originConfig/config dir to a user-writable path
  4. Create parent directories first (mkdir -p) since os.Mkdir only creates one level

Example fix

// before
sudo cloudflared tunnel token --cred-file /etc/cloudflared/token.json TUNNEL_ID
// after
sudo mkdir -p /etc/cloudflared && sudo chown $(id -u):$(id -g) /etc/cloudflared
cloudflared tunnel token --cred-file /etc/cloudflared/token.json TUNNEL_ID
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(configDir)
if err == nil && !info.IsDir() {
    return fmt.Errorf("%s exists and is not a directory", configDir)
}
if os.IsNotExist(err) {
    if err := os.MkdirAll(filepath.Dir(configDir), 0o755); err != nil {
        return err
    }
}

Try / catch

if err := ensureConfigDirExists(dir); err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) && errors.Is(pathErr.Err, syscall.EACCES) {
        return fmt.Errorf("no permission to create %s; run as root or choose another config dir", dir)
    }
    return err
}

Prevention

When it happens

Trigger: writeTokenToConfigDir or buildArgsForConfig calls ensureConfigDirExists while the parent of configDir is not writable, configDir exists as a regular file, or an intermediate path component is missing (os.Mkdir does not MkdirAll).

Common situations: Running cloudflared as a non-root user with config dir under /etc/cloudflared; a stale file named like the config dir; HOME pointing to a read-only or non-existent path in containers/systemd units.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/463f537a5c90b9d8. Report an issue: GitHub.