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
- Check ownership/permissions of the config directory path and chown/chmod it appropriately
- Ensure the path is a directory: `ls -ld <configDir>`; remove or rename a conflicting file
- Run with elevated privileges if the location requires root, or set the originConfig/config dir to a user-writable path
- 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
- Pre-create the config directory with correct ownership during provisioning
- Never shadow the config dir path with a regular file
- Remember os.Mkdir creates one level only; create parents with mkdir -p
- In containers, mount the config directory as a writable volume
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
- The file-writing error is: %v / The delete tunnel error is:
- create token file at %s: %w
- chmod token file at %s: %w
- failed to copy %s to %s: %w
- error creating %s: %v
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/463f537a5c90b9d8.
Report an issue: GitHub.