{"record":{"id":"8ed550ec4495535f","repo":"decolua/9router","slug":"failed-to-list-api-key-models-error","errorCode":null,"errorMessage":"Failed to list API-key models: ${error}","messagePattern":"Failed to list API-key models: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/kiro.js","lineNumber":316,"sourceCode":"   */\n  async listAvailableApiKeyModels(apiKey, region = \"us-east-1\") {\n    assertValidAwsRegion(region);\n    const params = new URLSearchParams({ origin: \"AI_EDITOR\" });\n    const endpoint = `https://q.${region}.amazonaws.com/ListAvailableModels?${params}`;\n    const response = await fetch(endpoint, {\n      method: \"GET\",\n      headers: {\n        \"Authorization\": `Bearer ${apiKey}`,\n        \"TokenType\": \"API_KEY\",\n        \"Accept\": \"application/json\",\n        \"User-Agent\": \"AWS-SDK-JS/3.0.0 kiro-ide/1.0.0\",\n        \"X-Amz-User-Agent\": \"aws-sdk-js/3.0.0 kiro-ide/1.0.0\",\n      },\n    });\n\n    if (!response.ok) {\n      const error = await response.text();\n      throw new Error(`Failed to list API-key models: ${error}`);\n    }\n\n    const data = await response.json();\n    const models = Array.isArray(data?.models) ? data.models : [];\n    if (models.length === 0) {\n      throw new Error(\"API key returned no available models\");\n    }\n    return models;\n  }\n\n  /**\n   * Validate an API-key credential through the same Amazon Q surface used for\n   * inference. API keys are account-bound but do not require a profileArn.\n   */\n  async validateApiKey(apiKey, region = \"us-east-1\") {\n    if (!apiKey || typeof apiKey !== \"string\" || !apiKey.trim()) {\n      throw new Error(\"API key is required\");\n    }","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/kiro.js#L298-L334","documentation":"Thrown by KiroService.listAvailableApiKeyModels when the Amazon Q ListAvailableModels call (GET https://q.<region>.amazonaws.com/ListAvailableModels?origin=AI_EDITOR with TokenType: API_KEY) returns non-2xx; the error body is embedded in the message. This is the validation probe for API-key accounts — a failure here means the key cannot be confirmed as inference-capable, so validateApiKey aborts.","triggerScenarios":"GET ListAvailableModels with Authorization: Bearer <apiKey> and TokenType: API_KEY returns !response.ok — malformed/invalid API key (401/403), key lacks Amazon Q model access, wrong region, or AWS throttling/5xx.","commonSituations":"Pasting an OAuth refresh token or access token where an API key is expected; API key revoked or from a different AWS partition; region without Amazon Q availability; corporate proxy intercepting the request; expired key after rotation.","solutions":["Verify you're using a Kiro/Amazon Q API key (not a refresh or access token) and that it wasn't truncated or whitespace-mangled on paste.","Read the embedded error body: 403 means the key lacks model access; 401 means the key is invalid/revoked.","Confirm the region hosts Amazon Q and matches the key's partition; try the default us-east-1.","Regenerate/rotate the API key in the Kiro/AWS console and retry; back off if throttled."],"exampleFix":"// before: validating a paste that mixed in an OAuth token\nconst ok = await svc.validateApiKey(userInput.trim());\n// after: pre-check that it looks like an API key\nconst key = userInput.trim();\nif (key.startsWith(\"aorAAAAAG\")) throw new Error(\"That is a refresh token, not an API key\");\nconst ok = await svc.validateApiKey(key);","handlingStrategy":"validation","validationCode":"// cheap client-side sanity checks before the network call\nfunction isPlausibleApiKey(k) {\n  return typeof k === 'string' && k.trim().length >= 20 && !k.includes(' ') &&\n         !k.startsWith('aorAAAAAG'); // that prefix marks a refresh token, not an API key\n}\nif (!isPlausibleApiKey(apiKey)) throw new Error('Value does not look like a Kiro/Amazon Q API key');","typeGuard":"function isModelList(d) { return Array.isArray(d?.models) && d.models.length > 0; }","tryCatchPattern":"try {\n  return await svc.validateApiKey(key);\n} catch (e) {\n  if (/401|Unauthorized/i.test(e.message)) showHelp('API key invalid or revoked — generate a new one');\n  else if (/403|AccessDenied/i.test(e.message)) showHelp('Key lacks Amazon Q model access');\n  else if (/throttl/i.test(e.message)) return retryWithBackoff();\n  else throw e;\n}","preventionTips":["Ensure the pasted value is an API key — a refresh token (aorAAAAAG…) or access token will be rejected with 401/403.","Trim whitespace/newlines from console pastes before validating.","Use a region where Amazon Q is available; us-east-1 is the safe default.","Treat this call as the authoritative key check — ListAvailableProfiles returns empty-200 for arbitrary keys, so don't substitute it.","Rotate keys on 401 after confirming the paste was correct."],"tags":["aws","api-key","amazon-q","validation","network"],"backgroundTag":"oauth-upstream-error","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}