Wei-Shaw/sub2api · error
xAI device approval did not reach done page
Error message
xAI device approval did not reach done page
What it means
After POST-ing approval (action=allow) to the approve endpoint, the flow requires the final URL to contain 'done'. If the approval POST returns 2xx/3xx but lands anywhere else, approval did not complete: the consent was denied server-side, a form field changed, or an intermediate step was inserted. Token polling is never started in this case.
Source
Thrown at backend/internal/pkg/xai/sso_device.go:174
}
if !strings.Contains(finalURL, "consent") {
return nil, errors.New("xAI device verification did not reach consent page")
}
status, finalURL, _, err = f.do(ctx, http.MethodPost, SSOApproveURL, url.Values{
"user_code": {device.UserCode},
"action": {"allow"},
"principal_type": {"User"},
"principal_id": {""},
})
if err != nil {
return nil, err
}
if status < 200 || status >= 400 {
return nil, fmt.Errorf("approve xAI device code: %w", SSOHTTPError{Status: status})
}
if !strings.Contains(finalURL, "done") {
return nil, errors.New("xAI device approval did not reach done page")
}
return f.pollToken(ctx, device.DeviceCode, time.Duration(device.Interval)*time.Second, time.Duration(device.ExpiresIn)*time.Second)
}
func (f *ssoDeviceFlow) pollToken(ctx context.Context, deviceCode string, interval, expiresIn time.Duration) (*TokenResponse, error) {
if interval < time.Second {
interval = time.Second
}
deadline := time.Now().Add(minDuration(expiresIn, 75*time.Second))
for time.Now().Before(deadline) {
if err := f.sleep(ctx, interval); err != nil {
return nil, err
}
status, _, body, err := f.do(ctx, http.MethodPost, SSOTokenURL, url.Values{
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
"client_id": {DefaultClientID},
"device_code": {deviceCode},View on GitHub (pinned to 073e92d171)
Solutions
- Retry the full flow with a fresh device code to rule out stale cookies/state.
- Log the final URL and response body to identify which step xAI inserted or renamed.
- Update the approve form values in sso_device.go if xAI changed required parameters.
- Confirm the account is a plain 'User' principal; if workspace selection is required, populate principal_id accordingly.
Example fix
// before
if !strings.Contains(finalURL, "done") {
return nil, errors.New("xAI device approval did not reach done page")
}
// after
if !strings.Contains(finalURL, "done") {
return nil, fmt.Errorf("xAI device approval did not reach done page (landed on %s)", finalURL)
} Defensive patterns
Strategy: retry
Try / catch
if err := flow.Authorize(ctx); err != nil {
if strings.Contains(err.Error(), "did not reach done page") {
return restartFlowWithFreshToken() // approval state lost; device_code is single-shot
}
return err
} Prevention
- Treat verify+approve as one unit; on any miss, restart the whole flow
- Keep cookies alive between verify and approve
- Monitor for xAI UI changes (alert on this error rate)
When it happens
Trigger: POST to SSOApproveURL with {user_code, action=allow, principal_type=User, principal_id=""} returns a redirect chain that ends on a non-'done' URL: changed form parameter names, an account-selection step now required (principal_id no longer empty), or consent actually declined.
Common situations: xAI alters the approve form (new hidden fields, renamed action value); org accounts requiring a real principal_id; cookie loss between verify and approve; A/B variants of the consent UI.
Related errors
- xAI device verification did not reach consent page
- xAI device flow response is incomplete
- xAI device flow token polling timed out
- xAI OAuth redirect missing Location
- xAI OAuth redirected to untrusted host
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/71960a554bd456a4.
Report an issue: GitHub.