netbirdio/netbird · error
access token response returned code: %s
Error message
access token response returned code: %s
What it means
Raised by DeviceAuthorizationFlow.requestToken when the IdP token endpoint answers with a status code above 499 (device_flow.go:236-238). The raw response body is embedded in the error text, so the message typically contains the IdP's HTML or JSON error payload. It signals a server-side failure (or a middlebox answering 5xx) during the device-flow token poll, as opposed to a protocol-level OAuth error, which arrives as 200/4xx with an error JSON body.
Source
Thrown at client/internal/auth/device_flow.go:237
res, err := d.HTTPClient.Do(req)
if err != nil {
return TokenRequestResponse{}, fmt.Errorf("failed to request access token with error: %v", err)
}
defer func() {
err := res.Body.Close()
if err != nil {
return
}
}()
body, err := io.ReadAll(res.Body)
if err != nil {
return TokenRequestResponse{}, fmt.Errorf("failed reading access token response body with error: %v", err)
}
if res.StatusCode > 499 {
return TokenRequestResponse{}, fmt.Errorf("access token response returned code: %s", string(body))
}
tokenResponse := TokenRequestResponse{}
err = json.Unmarshal(body, &tokenResponse)
if err != nil {
return TokenRequestResponse{}, fmt.Errorf("parsing token response failed with error: %v", err)
}
return tokenResponse, nil
}
// WaitToken waits user's login and authorize the app. Once the user's authorize
// it retrieves the access token from Hosted's endpoint and validates it before returning.
// The method creates a timeout context internally based on info.ExpiresIn.
func (d *DeviceAuthorizationFlow) WaitToken(ctx context.Context, info AuthFlowInfo) (TokenInfo, error) {
// Create timeout context based on flow expiration
timeout := time.Duration(info.ExpiresIn) * time.Second
waitCtx, cancel := context.WithTimeout(ctx, timeout)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Read the embedded body in the error message - it usually names the failing upstream component
- Retry netbird up after a short wait; 5xx bursts during IdP deploys are usually transient
- Check the IdP status page and the reverse-proxy logs in front of the token endpoint
- If persistent, verify the TokenEndpoint configured on the management side points to the correct, healthy IdP URL
Defensive patterns
Strategy: retry
Try / catch
token, err := flow.WaitToken(ctx, info)
if err != nil && strings.Contains(err.Error(), "access token response returned code:") {
// IdP/middlebox 5xx: wait and restart the flow rather than reporting a config problem
log.Warnf("IdP 5xx during token poll: %v", err)
time.Sleep(30 * time.Second)
return retryLogin(ctx)
} Prevention
- Monitor the IdP status page when 5xx bursts appear across multiple clients
- Read the embedded response body in the message - it identifies which component failed
- Do not misread this as a client misconfiguration; the request was well-formed
When it happens
Trigger: The token POST (client_id + device_code + grant_type) returns 500/502/503/504. Typical producers: IdP outage or maintenance, IdP gateway rate-limiting the polling, a reverse proxy in front of the IdP returning 502/504, or an auth service bug. Note this fires during WaitToken polling, so it can appear after the user already opened the verification URI.
Common situations: Auth0/Keycloak/Zitadel incidents or deploys, misconfigured reverse proxy (nginx/traefik) in front of the IdP timing out upstream, user polling during IdP maintenance windows.
Related errors
- failed reading access token response body with error: %v
- parsing token response failed with error: %v
- PKCE authorization flow failed: %v
- getting a request OAuth flow info failed: %v
- validate access token failed with error: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/477d3db59544934a.
Report an issue: GitHub.