cloudflare/cloudflared · error
failed to get app token
Error message
failed to get app token
What it means
This error wraps a client.Do failure during exchangeOrgToken, when the HEAD request to the application URL (used to obtain the app-token cookie) fails at the transport level. Thrown when DNS, connection, TLS, or the 7-second timeout fails; it is distinct from an HTTP error status, which is handled afterward by scanning cookies. On this failure the caller (getToken) typically falls back to re-fetching tokens from the edge.
Source
Thrown at token/token.go:566
// exchangeOrgToken attaches an org token to a request to the appURL and returns an app token. This uses the Access SSO
// flow to automatically generate and return an app token without the login page.
func exchangeOrgToken(appURL *url.URL, orgToken string) (string, error) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return handleRedirects(req, via, orgToken)
},
Timeout: time.Second * 7,
}
appTokenRequest, err := http.NewRequest("HEAD", appURL.String(), nil)
if err != nil {
return "", errors.Wrap(err, "failed to create app token request")
}
appTokenRequest.Header.Add(userAgentHeader, userAgent)
resp, err := client.Do(appTokenRequest) // nolint: gosec
if err != nil {
return "", errors.Wrap(err, "failed to get app token")
}
_ = resp.Body.Close()
var appToken string
for _, c := range resp.Cookies() {
//if Org token revoked on exchange, getTokensFromEdge instead
validAppToken := c.Name == tokenCookie && time.Now().Before(c.Expires)
if validAppToken {
appToken = c.Value
break
}
}
if len(appToken) > 0 {
return appToken, nil
}
return "", fmt.Errorf("response from %s did not contain app token", resp.Request.URL.String())
}
View on GitHub (pinned to 2253eeeb25)
Solutions
- Check reachability of the app URL (curl -I) and DNS from the client machine
- If the org token is stale, remove the cached org token and re-run cloudflared access login to get tokens from the edge directly
- Retry on transient network errors; investigate proxy/VPN interference
- If timeouts recur, check origin latency behind the Access application
Defensive patterns
Strategy: retry
Validate before calling
conn, err := net.DialTimeout("tcp", appURL.Hostname()+":443", 3*time.Second)
if err != nil {
// app host unreachable; fall back to getTokensFromEdge
}
conn.Close() Try / catch
for i := 0; i < 3; i++ {
appToken, err := exchangeOrgToken(ctx, appURL, log)
if err == nil {
break
}
if isTimeout(err) {
time.Sleep(2 * time.Second)
continue
}
// non-transient: fall back to full edge token fetch
appToken, err = getTokensFromEdge(ctx, log)
break
} Prevention
- Handle the 7-second timeout explicitly in retries
- Fall back to getTokensFromEdge when exchange repeatedly fails
- Check connectivity to the app origin before exchanging tokens
- Keep org tokens fresh; expired org tokens lead to failed exchanges
When it happens
Trigger: exchangeOrgToken calls client.Do(appTokenRequest) and gets a transport error: app host unreachable, connection refused, TLS handshake failure, or request exceeding the 7-second client timeout.
Common situations: Application hostname is internal-only and unreachable from the client machine; network interruption mid-session; slow origin causing repeated timeouts; corporate proxy blocking the request.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- failed to get app info
- Failed to fetch resource
- quick tunnel provisioning failed with status %d: %s
- quick tunnel provisioning failed: %s
- unable to check for update: %d
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/b333b4b7f5323776.
Report an issue: GitHub.