cloudflare/cloudflared · error

chmod token file at %s: %w

Error message

chmod token file at %s: %w

What it means

After opening the token file, createTokenFileUnix calls os.Chmod(path, 0600) because OpenFile will not tighten permissions on a pre-existing file. If the chmod fails, the error is wrapped as 'chmod token file at <path>: <cause>'. Leaving the file with loose permissions would expose the tunnel credential, so this is deliberately fatal.

Source

Thrown at cmd/cloudflared/common_service.go:41

			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)
	}

	return nil
}

// Write out the token file to the configuration directory with the correct
// permissions. Since the method used to restrict the permissions is platform
// dependent, make the function used to restrict the permissions an injectable
// dependency
func writeTokenToFile(path string, token string) error {
	if _, err := tunnel.ParseToken(token); err != nil {
		return cliutil.UsageError("Provided tunnel token is not valid (%s).", err)
	}

	if err := createTokenFile(path); err != nil {
		return fmt.Errorf("create token file at %s: %w", path, err)
	}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. chown the existing token file to the user running cloudflared, or delete it and rerun so it is recreated with 0600
  2. Remove immutable/append-only attributes: `chattr -i <path>`
  3. Run as the same (privileged) user that originally created the file
  4. Avoid sharing the token path across users; give each principal its own cred-file

Example fix

// before
sudo cloudflared tunnel token --cred-file /etc/cloudflared/token.json TUNNEL_ID   # file now root-owned
cloudflared tunnel token --cred-file /etc/cloudflared/token.json TUNNEL_ID        # chmod fails
// after
sudo chown $(id -u):$(id -g) /etc/cloudflared/token.json
cloudflared tunnel token --cred-file /etc/cloudflared/token.json TUNNEL_ID
Defensive patterns

Strategy: try-catch

Validate before calling

if info, err := os.Stat(path); err == nil && info.Mode().Perm() != 0o600 {
    if err := os.Chmod(path, 0o600); err != nil {
        return fmt.Errorf("cannot tighten permissions on %s: %w", path, err)
    }
}

Try / catch

if err := createTokenFileUnix(path); err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) && errors.Is(pathErr.Err, syscall.EPERM) {
        return fmt.Errorf("cannot chmod %s (owned by someone else?); remove it and rerun as the current user", path)
    }
    return err
}

Prevention

When it happens

Trigger: The token file already exists but is owned by another user (created previously by root), so the current user cannot chmod it; the filesystem does not support chmod; ACLs/immutable attributes block the change.

Common situations: First run as root created the token file, later runs as a service user attempt to refresh it; an immutable flag (chattr +i) was set on the credential file; NFS exports squashing ownership.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


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