{"record":{"id":"e9d4c9801c4f67ba","repo":"chatboxai/chatbox","slug":"qwen-device-authorization-failed-text","errorCode":null,"errorMessage":"Qwen device authorization failed: ${text}","messagePattern":"Qwen device authorization failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/oauth/providers/qwen.ts","lineNumber":73,"sourceCode":"\n    const response = await fetch(`${QWEN_BASE_URL}/api/v1/oauth2/device/code`, {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/x-www-form-urlencoded',\n        'x-request-id': randomUUID(),\n      },\n      body: new URLSearchParams({\n        client_id: QWEN_CLIENT_ID,\n        scope: 'openid profile email model.completion',\n        code_challenge: challenge,\n        code_challenge_method: 'S256',\n      }).toString(),\n    })\n\n    if (!response.ok) {\n      const text = await response.text()\n      throw new Error(`Qwen device authorization failed: ${text}`)\n    }\n\n    const payload = (await response.json()) as {\n      device_code: string\n      user_code: string\n      verification_uri: string\n      verification_uri_complete?: string\n      expires_in?: number\n      interval?: number\n    }\n\n    pendingDeviceCode = payload.device_code\n    pendingVerifier = verifier\n    pendingIntervalMs = payload.interval ? payload.interval * 1000 : 2000\n\n    return {\n      userCode: payload.user_code,\n      verificationUri: payload.verification_uri_complete || payload.verification_uri,","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/oauth/providers/qwen.ts#L55-L91","documentation":"Thrown by Qwen's startDeviceFlow() when the initial POST to chat.qwen.ai/api/v1/oauth2/device/code returns non-2xx. The raw response body is interpolated. This is the first call that registers the device flow and yields the user_code and verification_uri; failure here prevents polling from ever starting.","triggerScenarios":"QWEN_CLIENT_ID (f0304373b74a44d2b584a3fb70ca9e56) rejected by Qwen; requested scope (openid profile email model.completion) not permitted for the client; PKCE code_challenge malformed or not S256; QWEN_BASE_URL changed; x-request-id header rejected; Qwen API outage or rate limit.","commonSituations":"Qwen rotated the public client_id; the base URL shifted (e.g. regional endpoint); a corporate proxy rewrote the form body or stripped the x-request-id header; the scope set was changed server-side.","solutions":["Read the interpolated body for Qwen's reason — 4xx typically means client/scope/PKCE mismatch, 5xx means retry.","Verify QWEN_CLIENT_ID and QWEN_BASE_URL against the current Qwen portal contract.","Confirm code_challenge is base64url SHA-256 of the verifier and code_challenge_method is 'S256'.","For 5xx or network errors, retry once with backoff before surfacing."],"exampleFix":"// before\nif (!response.ok) {\n  const text = await response.text()\n  throw new Error(`Qwen device authorization failed: ${text}`)\n}\n\n// after\nif (!response.ok) {\n  const text = await response.text()\n  log.error('[OAuth:Qwen] device code request failed', response.status, text)\n  if (response.status >= 500) throw new TransientError('Qwen unavailable, retry')\n  throw new Error(`Qwen device authorization failed (${response.status}): ${text}`)\n}","handlingStrategy":"try-catch","validationCode":"// Sanity-check PKCE before the request to Qwen.\nfunction assertQwenPkce(verifier: string, challenge: string) {\n  if (verifier.length < 32) throw new Error('verifier too short')\n  if (!/^[A-Za-z0-9_-]+$/.test(challenge)) throw new Error('challenge must be base64url')\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await provider.startDeviceFlow()\n} catch (e) {\n  const msg = String(e)\n  if (/Qwen device authorization failed/i.test(msg)) {\n  if (/5\\d\\d|network|fetch/i.test(msg)) return await provider.startDeviceFlow() // transient retry\n  throw new Error('Qwen rejected the device request — check client_id, scope, base URL, and PKCE.')\n  }\n  throw e\n}","preventionTips":["Verify QWEN_CLIENT_ID and QWEN_BASE_URL against the current Qwen portal contract after each release.","Generate the challenge as base64url SHA-256 of the verifier with method S256.","Keep a unique x-request-id per request to aid server-side tracing.","Retry only on 5xx/network; 4xx indicates config or contract problems."],"tags":["oauth","network","qwen","device-flow","pkce"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}