{"record":{"id":"b8d9a16bba623293","repo":"cloudflare/cloudflared","slug":"failed-to-write-app-token-to-disk","errorCode":null,"errorMessage":"failed to write app token to disk","messagePattern":"failed to write app token to disk","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/token.go","lineNumber":380,"sourceCode":"\t\tif err != nil {\n\t\t\treturn \"\", errors.Wrap(err, \"failed to generate org token file path\")\n\t\t}\n\n\t\torgTokenLock, orgLockErr := acquireLockFile(orgTokenPath, log)\n\t\tif orgLockErr != nil {\n\t\t\treturn \"\", errors.Wrap(orgLockErr, \"failed to acquire org token lock\")\n\t\t}\n\t\tdefer orgTokenLock.release()\n\t\t// check if an org token has been created since the lock was acquired\n\t\torgToken, orgTokenErr = GetOrgTokenIfExists(appInfo.AuthDomain)\n\t}\n\tif orgTokenErr == nil {\n\t\tif appToken, exchangeErr := exchangeOrgToken(appURL, orgToken); exchangeErr != nil {\n\t\t\tlog.Debug().Msgf(\"failed to exchange org token for app token: %s\", exchangeErr)\n\t\t} else {\n\t\t\t// generate app path\n\t\t\tif err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil { // nolint: gosec\n\t\t\t\treturn \"\", errors.Wrap(err, \"failed to write app token to disk\")\n\t\t\t}\n\t\t\treturn appToken, nil\n\t\t}\n\t}\n\treturn getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, isFedramp, log)\n}\n\n// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,\n// and return the app token.\nfunc getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {\n\t// If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow.\n\n\t// this weird parameter is the resource name (token) and the key/value\n\t// we want to send to the transfer service. the key is token and the value\n\t// is blank (basically just the id generated in the transfer service)\n\tresourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, \"\", true, useHostOnly, autoClose, isFedramp, log, appTokenPath+\".url\")\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to run transfer service\")","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/token.go#L362-L398","documentation":"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.","triggerScenarios":"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).","commonSituations":"~/.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.","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"],"exampleFix":"// before: write fails silently into a deleted directory\nif err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {\n\treturn \"\", errors.Wrap(err, \"failed to write app token to disk\")\n}\n// after: ensure the directory exists immediately before the write\nif err := os.MkdirAll(filepath.Dir(appTokenPath), 0700); err != nil {\n\treturn \"\", errors.Wrap(err, \"failed to ensure token directory\")\n}\nif err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {\n\treturn \"\", errors.Wrap(err, \"failed to write app token to disk\")\n}","handlingStrategy":"try-catch","validationCode":"// check the directory exists and is writable right before the write\nif err := syscall.Access(filepath.Dir(appTokenPath), syscall.W_OK); err != nil {\n\tos.MkdirAll(filepath.Dir(appTokenPath), 0700)\n}","typeGuard":null,"tryCatchPattern":"token, err := FetchToken(...)\nif err != nil && strings.Contains(err.Error(), \"failed to write app token to disk\") {\n\t// exchange succeeded; a one-shot retry usually recovers\n\terr = ensureTokenDir(); if err == nil { token, err = FetchToken(...) }\n}","preventionTips":["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"],"tags":["filesystem","token","file-write","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}