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

  1. Check reachability of the app URL (curl -I) and DNS from the client machine
  2. If the org token is stale, remove the cached org token and re-run cloudflared access login to get tokens from the edge directly
  3. Retry on transient network errors; investigate proxy/VPN interference
  4. 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

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


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