router-for-me/CLIProxyAPI · error

xai device code: response missing verification URI

Error message

xai device code: response missing verification URI

What it means

Neither verification_uri nor verification_uri_complete was present in the device authorization response. The user needs one of these URIs to enter their user_code and approve the request; the guard fires only when both are empty because either one is sufficient for the flow.

Solutions

  1. Capture and inspect the raw response body to confirm which fields the server actually sent
  2. Validate the mock/staging server implements verification_uri (or verification_uri_complete) if developing against one
  3. Re-run discovery (a.Discover) and confirm endpoints are current xAI endpoints, not cached stale URLs
Defensive patterns

Strategy: validation

Try / catch

deviceCode, err := auth.RequestDeviceCode(ctx)
if err != nil && strings.Contains(err.Error(), "missing verification URI") {
    log.Warnf("cannot direct user to consent page: %v", err)
    return err
}

Prevention

When it happens

Trigger: 200 JSON from the xAI device authorization endpoint where both verification_uri and verification_uri_complete are missing/whitespace, while device_code/user_code checks already passed.

Common situations: xAI response schema change; API gateway or HTML error page returned with 200; testing against a mock server that only implements part of the device-authorization contract.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/f29f31a49a9aeecb. Report an issue: GitHub.

Appendix: source

Thrown at internal/auth/xai/xai.go:172

	if err != nil {
		return nil, fmt.Errorf("xai device code: read response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("xai device code request failed with status %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
	}

	var deviceCode DeviceCodeResponse
	if err = json.Unmarshal(body, &deviceCode); err != nil {
		return nil, fmt.Errorf("xai device code: parse response: %w", err)
	}
	if strings.TrimSpace(deviceCode.DeviceCode) == "" {
		return nil, fmt.Errorf("xai device code: response missing device_code")
	}
	if strings.TrimSpace(deviceCode.UserCode) == "" {
		return nil, fmt.Errorf("xai device code: response missing user_code")
	}
	if strings.TrimSpace(deviceCode.VerificationURI) == "" && strings.TrimSpace(deviceCode.VerificationURIComplete) == "" {
		return nil, fmt.Errorf("xai device code: response missing verification URI")
	}
	deviceCode.TokenEndpoint = strings.TrimSpace(tokenEndpoint)
	return &deviceCode, nil
}

// WaitForAuthorization polls until the user authorizes the device code and returns tokens.
func (a *XAIAuth) WaitForAuthorization(ctx context.Context, deviceCode *DeviceCodeResponse) (*AuthBundle, error) {
	tokenData, err := a.PollForToken(ctx, deviceCode)
	if err != nil {
		return nil, err
	}
	tokenEndpoint := ""
	if deviceCode != nil {
		tokenEndpoint = strings.TrimSpace(deviceCode.TokenEndpoint)
	}
	return &AuthBundle{
		TokenData:     *tokenData,
		LastRefresh:   time.Now().UTC().Format(time.RFC3339),

View on GitHub (pinned to 78f0c4079e)