odysseus-dev/odysseus · error · HTTPException

ChatGPT did not return a complete device code

Error message

ChatGPT did not return a complete device code

What it means

Raised as HTTP 502 by _start_device_flow when the device-authorization response from the ChatGPT OAuth issuer lacks device_auth_id or user_code. The upstream call succeeded but the payload is incomplete, so the server cannot build a pending flow for the user to approve. It is explicitly a bad-gateway classification: the fault is attributed to the upstream issuer response.

Source

Thrown at routes/chatgpt_subscription_routes.py:118

    try:
        from routes.model_routes import _invalidate_models_cache

        _invalidate_models_cache()
    except Exception:
        pass
    return result


def _start_device_flow(request: Request, _form) -> DeviceFlowStart:
    try:
        data = chatgpt_subscription.request_device_code()
    except Exception as exc:
        raise chatgpt_subscription.to_http_exception(exc)

    device_auth_id = data.get("device_auth_id")
    user_code = data.get("user_code")
    if not device_auth_id or not user_code:
        raise HTTPException(502, "ChatGPT did not return a complete device code")
    verification_uri = data.get("verification_uri") or f"{chatgpt_subscription.CHATGPT_OAUTH_ISSUER}/codex/device"
    return DeviceFlowStart(
        pending={
            "device_auth_id": device_auth_id,
            "user_code": user_code,
            "owner": get_current_user(request) or None,
        },
        response={
            "user_code": user_code,
            "verification_uri": verification_uri,
        },
        interval=int(data.get("interval") or 5),
        expires_in=int(data.get("expires_in") or 900),
    )


def _poll_device_flow(_request: Request, pending: Dict) -> DeviceFlowPoll:
    try:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Retry the device-flow start once — transient issuer glitches are the most common cause.
  2. Update the app to the latest release so chatgpt_subscription.request_device_code matches the current issuer response schema.
  3. Inspect server logs for the raw device-code response to see which key is missing (device_auth_id vs user_code points at different upstream changes).
  4. Remove any HTTP proxy between the server and the ChatGPT OAuth issuer and retry.
Defensive patterns

Strategy: retry

Type guard

def is_complete_device_code(data: dict) -> bool:
    return bool(data.get('device_auth_id')) and bool(data.get('user_code'))

Try / catch

for attempt in range(2):
    resp = requests.post(f'{BASE}/api/chatgpt/device/start')
    if resp.status_code == 502 and attempt == 0:
        time.sleep(1)
        continue
    resp.raise_for_status()
    break

Prevention

When it happens

Trigger: Starting a ChatGPT Subscription connection (GET/POST of the device-flow start endpoint) while the issuer returns an error envelope, changes field names, or the response is truncated/re-serialized by a proxy.

Common situations: OpenAI ships a breaking change to the device authorization endpoint; a corporate proxy rewrites the JSON; an outdated bundled version of chatgpt_subscription expects fields the issuer no longer returns; transient issuer degradation.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/0be427f839f37d56. Report an issue: GitHub.