amir20/dozzle · error
token exchange failed
Error message
token exchange failed
What it means
cloudCallback requires the token-exchange endpoint to answer HTTP 200. Any other status (4xx/5xx from the cloud service) is turned into a 500 with this generic message; the real status and first 1KB of the response body are only visible in dozzle's server logs under `Token exchange failed`.
Solutions
- Check dozzle server logs for the `Token exchange failed` entry with the actual status and response body.
- Restart the cloud connect flow to get a fresh, unused token.
- If status is 401/403, verify the token was not modified in transit and the cloud account/API key is valid.
- If status is 5xx, wait and retry; the problem is on the cloud service side.
Example fix
// before (log)
{"level":"error","status":401,"body":"invalid token"}
// after
restart cloud connect flow from UI to obtain a fresh token Defensive patterns
Strategy: fallback
Try / catch
const res = await fetch(exchangeUrl, { method: 'POST' });
if (!res.ok) {
const body = await res.text();
console.error('exchange failed', res.status, body); // check dozzle logs for the real status
} Prevention
- Check dozzle server logs for the actual upstream status and body.
- Do not reuse old callback URLs; always obtain a fresh token.
- Monitor the cloud service status during outages.
When it happens
Trigger: The POST to /api/exchange-token returns a non-200 status, e.g. 401/403 for an invalid or expired callback token, 410 for a used/stale token, or 5xx from the cloud service itself.
Common situations: Replaying an old callback URL whose one-time token was already consumed; tampered or expired token in the redirect; cloud service outage; clock skew invalidating a short-lived token.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Failed to save alert
- Preview failed
- cloud search failed
- Failed to save destination
- dispatchers fetch failed
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/b5eea3437c94f18e.
Report an issue: GitHub.
Appendix: source
Thrown at internal/web/cloud.go:82
return
}
req.Header.Set("User-Agent", dispatcher.UserAgent)
q := req.URL.Query()
q.Set("token", token)
req.URL.RawQuery = q.Encode()
resp, err := client.Do(req)
if err != nil {
log.Error().Err(err).Msg("Failed to exchange token")
http.Error(w, "failed to exchange token", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
log.Error().Int("status", resp.StatusCode).Str("body", string(body)).Msg("Token exchange failed")
http.Error(w, "token exchange failed", http.StatusInternalServerError)
return
}
var tokenResp exchangeTokenResponse
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
log.Error().Err(err).Msg("Failed to decode token response")
http.Error(w, "failed to decode token response", http.StatusInternalServerError)
return
}
if tokenResp.Key == "" {
log.Error().Msg("Empty key received")
http.Error(w, "empty key received", http.StatusInternalServerError)
return
}
var expiresAt *time.Time
if tokenResp.ExpiresAt != nil {View on GitHub (pinned to d9463cbe21)