cloudflare/cloudflared · error
failed to write app token to disk
Error message
failed to write app token to disk
What it means
getToken returns this error when the org token was successfully exchanged for an app token, but writing the resulting app token to appTokenPath via os.WriteFile fails. This is the final persistence step of the exchange flow; if the write fails, the caller receives no token even though the exchange succeeded.
Source
Thrown at token/token.go:380
if err != nil {
return "", errors.Wrap(err, "failed to generate org token file path")
}
orgTokenLock, orgLockErr := acquireLockFile(orgTokenPath, log)
if orgLockErr != nil {
return "", errors.Wrap(orgLockErr, "failed to acquire org token lock")
}
defer orgTokenLock.release()
// check if an org token has been created since the lock was acquired
orgToken, orgTokenErr = GetOrgTokenIfExists(appInfo.AuthDomain)
}
if orgTokenErr == nil {
if appToken, exchangeErr := exchangeOrgToken(appURL, orgToken); exchangeErr != nil {
log.Debug().Msgf("failed to exchange org token for app token: %s", exchangeErr)
} else {
// generate app path
if err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil { // nolint: gosec
return "", errors.Wrap(err, "failed to write app token to disk")
}
return appToken, nil
}
}
return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, isFedramp, log)
}
// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
// and return the app token.
func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
// If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow.
// this weird parameter is the resource name (token) and the key/value
// we want to send to the transfer service. the key is token and the value
// is blank (basically just the id generated in the transfer service)
resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, autoClose, isFedramp, log, appTokenPath+".url")
if err != nil {
return "", errors.Wrap(err, "failed to run transfer service")View on GitHub (pinned to 2253eeeb25)
Solutions
- Verify ~/.cloudflared exists and is writable right now: ls -ld ~/.cloudflared; recreate it if removed
- Check disk space (df -h) and inode/quota limits
- Look for security software or cleanup jobs interfering with token file writes
- Retry the token fetch once the directory is stable; the exchange itself succeeded so the retry should be quick
Example fix
// before: write fails silently into a deleted directory
if err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {
return "", errors.Wrap(err, "failed to write app token to disk")
}
// after: ensure the directory exists immediately before the write
if err := os.MkdirAll(filepath.Dir(appTokenPath), 0700); err != nil {
return "", errors.Wrap(err, "failed to ensure token directory")
}
if err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {
return "", errors.Wrap(err, "failed to write app token to disk")
} Defensive patterns
Strategy: try-catch
Validate before calling
// check the directory exists and is writable right before the write
if err := syscall.Access(filepath.Dir(appTokenPath), syscall.W_OK); err != nil {
os.MkdirAll(filepath.Dir(appTokenPath), 0700)
} Try / catch
token, err := FetchToken(...)
if err != nil && strings.Contains(err.Error(), "failed to write app token to disk") {
// exchange succeeded; a one-shot retry usually recovers
err = ensureTokenDir(); if err == nil { token, err = FetchToken(...) }
} Prevention
- Exclude ~/.cloudflared from tmp-cleanup and AV scans
- Keep the token dir on a stable local (non-tmpfs) filesystem
- MkdirAll the directory before each token operation
- Monitor disk quotas for the service user
When it happens
Trigger: FetchToken -> getToken -> exchangeOrgToken succeeds, then os.WriteFile(appTokenPath, ...) fails: directory removed between lock acquisition and write, permission change, disk full, or the path becoming invalid (e.g. too long after hostname changes).
Common situations: ~/.cloudflared deleted or chmod'd while cloudflared was running (common with tmp-cleanup jobs); quota exhaustion; AV/EDR software blocking writes of token-like files; concurrent cleanup removing the directory the lock was held in.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- error writing %s: %v
- failed to create lock file %s
- failed to acquire app token lock
- failed to acquire org token lock
- failed to write org token to disk
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/b8d9a16bba623293.
Report an issue: GitHub.