{"record":{"id":"30dff0e96ac934c4","repo":"decolua/9router","slug":"failed-to-start-device-authorization-error","errorCode":null,"errorMessage":"Failed to start device authorization: ${error}","messagePattern":"Failed to start device authorization: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/kiro.js","lineNumber":71,"sourceCode":"  async startDeviceAuthorization(clientId, clientSecret, startUrl, region = \"us-east-1\") {\n    assertValidAwsRegion(region);\n    const endpoint = `https://oidc.${region}.amazonaws.com/device_authorization`;\n\n    const response = await fetch(endpoint, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        clientId,\n        clientSecret,\n        startUrl,\n      }),\n    });\n\n    if (!response.ok) {\n      const error = await response.text();\n      throw new Error(`Failed to start device authorization: ${error}`);\n    }\n\n    const data = await response.json();\n    return {\n      deviceCode: data.deviceCode,\n      userCode: data.userCode,\n      verificationUri: data.verificationUri,\n      verificationUriComplete: data.verificationUriComplete,\n      expiresIn: data.expiresIn,\n      interval: data.interval || 5,\n    };\n  }\n\n  /**\n   * Poll for token using device code (AWS Builder ID/IDC)\n   */\n  async pollDeviceToken(clientId, clientSecret, deviceCode, region = \"us-east-1\") {\n    assertValidAwsRegion(region);","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/kiro.js#L53-L89","documentation":"Thrown by KiroService.startDeviceAuthorization when the AWS SSO OIDC device_authorization endpoint returns non-2xx. The response body (AWS error JSON) is embedded in the message. This step creates the deviceCode/userCode pair, so failure aborts the Builder ID / IDC device login before the user ever sees a verification URL.","triggerScenarios":"POST to oidc.<region>.amazonaws.com/device_authorization with clientId/clientSecret/startUrl returns !response.ok — most often an invalid or expired clientId/clientSecret from a previous registerClient, or an unknown region.","commonSituations":"Reusing cached client credentials after their clientSecretExpiresAt passed; registerClient previously failed partially and stale credentials were persisted; region mismatch between registration and device-authorization calls; AWS 429 throttling.","solutions":["Call registerClient again to obtain fresh clientId/clientSecret and retry — expired client secrets are the most common cause.","Check the error body in the message for invalid_client / invalidRequest details.","Ensure the same region is used for registerClient and startDeviceAuthorization.","Back off and retry if the body shows throttling."],"exampleFix":"// before: reusing long-lived cached client creds\nawait svc.startDeviceAuthorization(saved.clientId, saved.clientSecret, startUrl);\n// after: re-register when the client secret expired\nconst client = saved.clientSecretExpiresAt > Date.now()/1000\n  ? saved\n  : await svc.registerClient(region);\nawait svc.startDeviceAuthorization(client.clientId, client.clientSecret, startUrl, region);","handlingStrategy":"retry","validationCode":"function hasClientCreds(c) {\n  return typeof c?.clientId === 'string' && c.clientId.length > 0 &&\n         typeof c?.clientSecret === 'string' && c.clientSecret.length > 0 &&\n         (!c.clientSecretExpiresAt || c.clientSecretExpiresAt * 1000 > Date.now());\n}\nif (!hasClientCreds(saved)) throw new Error('Client credentials missing or expired — call registerClient first');","typeGuard":"function isDeviceAuthResult(d) { return typeof d?.deviceCode === 'string' && typeof d?.userCode === 'string' && typeof d?.verificationUri === 'string'; }","tryCatchPattern":"try {\n  return await svc.startDeviceAuthorization(clientId, clientSecret, startUrl, region);\n} catch (e) {\n  if (/invalid_client|expired/i.test(e.message)) {\n    const c = await svc.registerClient(region);\n    return svc.startDeviceAuthorization(c.clientId, c.clientSecret, startUrl, region);\n  }\n  throw e;\n}","preventionTips":["Re-register the OIDC client whenever clientSecretExpiresAt has passed instead of reusing cached credentials.","Keep registration and device-authorization in the same region.","Persist clientId/clientSecret together as one unit to avoid partial-state bugs.","Handle 429 with exponential backoff before surfacing the error."],"tags":["oauth","aws","oidc","device-flow","network"],"backgroundTag":"oauth-upstream-error","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}