Wei-Shaw/sub2api · error

xAI device flow response is incomplete

Error message

xAI device flow response is incomplete

What it means

Returned by the xAI SSO device flow when the initial POST to the device authorization endpoint yields a 200-ish body that either fails to deserialize or is missing required fields: device_code, user_code, or a safe verification_uri_complete. The flow cannot continue without all three because the subsequent verify/approve steps key off them.

Source

Thrown at backend/internal/pkg/xai/sso_device.go:133

	})
	if err != nil {
		return nil, err
	}
	if status < 200 || status >= 300 {
		return nil, fmt.Errorf("start xAI device flow: %w", SSOHTTPError{Status: status})
	}
	var device struct {
		DeviceCode              string `json:"device_code"`
		UserCode                string `json:"user_code"`
		VerificationURIComplete string `json:"verification_uri_complete"`
		Interval                int    `json:"interval"`
		ExpiresIn               int    `json:"expires_in"`
	}
	if err := json.Unmarshal(body, &device); err != nil {
		return nil, fmt.Errorf("parse xAI device flow response: %w", err)
	}
	if device.DeviceCode == "" || device.UserCode == "" || !safeXAIAuthURL(device.VerificationURIComplete) {
		return nil, errors.New("xAI device flow response is incomplete")
	}
	if device.Interval <= 0 {
		device.Interval = 5
	}
	if device.ExpiresIn <= 0 {
		device.ExpiresIn = 1800
	}

	status, _, _, err = f.do(ctx, http.MethodGet, device.VerificationURIComplete, nil)
	if err != nil {
		return nil, err
	}
	if status < 200 || status >= 400 {
		return nil, fmt.Errorf("open xAI device verification page: %w", SSOHTTPError{Status: status})
	}

	status, finalURL, _, err = f.do(ctx, http.MethodPost, SSOVerifyURL, url.Values{"user_code": {device.UserCode}})
	if err != nil {

View on GitHub (pinned to 073e92d171)

Solutions

  1. Capture the raw response body (temporarily log it) and compare against the expected {device_code, user_code, verification_uri_complete} shape.
  2. Retry once after a short delay to rule out a transient page/interstitial.
  3. If the schema genuinely changed, update the device struct field names/tags in sso_device.go.
  4. Bypass any corporate proxy for accounts.x.ai / xai.com OAuth hosts.

Example fix

// before
if err := json.Unmarshal(body, &device); err != nil {
    return nil, fmt.Errorf("parse xAI device flow response: %w", err)
}
if device.DeviceCode == "" || device.UserCode == "" || !safeXAIAuthURL(device.VerificationURIComplete) {
    return nil, errors.New("xAI device flow response is incomplete")
}

// after (debugging aid: include a body excerpt in the error)
const sniff = 256
excerpt := string(body)
if len(excerpt) > sniff { excerpt = excerpt[:sniff] }
return nil, fmt.Errorf("xAI device flow response is incomplete (body starts: %s)", excerpt)
Defensive patterns

Strategy: retry

Try / catch

var device *xai.DeviceCode
err := retry.Do(func() error {
    var e error
    device, e = flow.Start(ctx)
    return e
}, retry.RetryIf(func(e error) bool {
    return errors.Is(e, xai.ErrDeviceFlowIncomplete) // transient schema/page hiccups
}), retry.Attempts(2))

Prevention

When it happens

Trigger: xAI changes or breaks its device authorization response schema (renames fields, returns an HTML error page with 200, or returns an empty object); or a MITM/proxy rewrites the response. JSON that parses but lacks any of the three required fields triggers this exact error.

Common situations: xAI ships an API change to the undocumented device-flow endpoint; Cloudflare interstitials returning HTML; corporate proxies mangling responses; regional variants of the auth host serving a different payload.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/fb81572474e14896. Report an issue: GitHub.