chatboxai/chatbox · error · Error

Qwen OAuth timed out waiting for authorization.

Error message

Qwen OAuth timed out waiting for authorization.

What it means

Thrown when the device-flow polling loop exits because Date.now() passed the 10-minute deadline (Date.now() < deadline became false) without ever receiving an access_token. It is a pure client-side timeout, distinct from [80] which requires the server to return an explicit error. The deadline is set at the start of authenticate() to Date.now() + 10*60*1000.

Source

Thrown at src/main/oauth/providers/qwen.ts:156

        } else {
          const payload = (await response.json().catch(() => ({}))) as {
            error?: string
            error_description?: string
          }

          if (payload.error === 'authorization_pending') {
            continue
          }
          if (payload.error === 'slow_down') {
            intervalMs = Math.min(intervalMs + 2000, 10_000)
            continue
          }

          throw new Error(`Qwen OAuth failed: ${payload.error_description || payload.error || response.statusText}`)
        }
      }

      throw new Error('Qwen OAuth timed out waiting for authorization.')
    } finally {
      pendingDeviceCode = null
      pendingVerifier = null
      pendingIntervalMs = 2000
    }
  },

  async refreshToken(credentials) {
    if (!credentials.refreshToken) {
      log.warn('[OAuth:Qwen] No refresh token available, returning existing credentials')
      return credentials
    }

    const response = await fetch(`${QWEN_BASE_URL}/api/v1/oauth2/token`, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',

View on GitHub (pinned to 81571269ad)

Solutions

  1. Treat it as user-facing: show 'Authorization timed out' and offer a 'Try again' button that calls authenticate() again for a fresh device code.
  2. Verify the verification_uri (and user_code) were actually displayed to the user — a UI bug that never renders the prompt produces this every time.
  3. If polls seem too infrequent, confirm pendingIntervalMs starts at 2000 and slow_down increments are honored; do not lower the cap below the server's advised interval.
  4. Check for a hung signal — if the AbortSignal was already aborted, abortableSleep rejects and the loop never completes a real poll.

Example fix

try {
  return await qwenProvider.authenticate(signal)
} catch (e) {
  if (e instanceof Error && e.message === 'Qwen OAuth timed out waiting for authorization.') {
    // prompt user to retry; re-initiate device flow
  }
  throw e
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

function isQwenTimeout(e: unknown): e is Error {
  return e instanceof Error && e.message === 'Qwen OAuth timed out waiting for authorization.'
}

Try / catch

try { return await qwenProvider.authenticate(signal) }
catch (e) { if (isQwenTimeout(e)) { ui.notify('Authorization timed out, please retry'); return qwenProvider.authenticate(signal) } throw e }

Prevention

When it happens

Trigger: The user never opens the verification URL, or completes verification but the poll interval (starting 2000ms, capped 10000ms after slow_down) plus each abortableSleep never lands a successful token within 600s. Also reachable if the signal is never aborted but the server only ever returns authorization_pending until the deadline.

Common situations: User walks away from the device-authorization prompt; user completed login on the wrong account / wrong region; network is so slow that every fetch eats the interval and the deadline elapses before enough polls; the UI that shows the verification_uri closed before the user acted.

Understand the failure class

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/887cbbc61976111b. Report an issue: GitHub.