{"record":{"id":"3f46ba96e18c369f","repo":"decolua/9router","slug":"api-key-is-required","errorCode":null,"errorMessage":"API key is required","messagePattern":"API key is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lib/oauth/services/kiro.js","lineNumber":333,"sourceCode":"      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    }\n    const trimmed = apiKey.trim();\n\n    try {\n      await this.listAvailableApiKeyModels(trimmed, region);\n    } catch (error) {\n      throw new Error(`API key validation failed: ${error.message}`);\n    }\n\n    return {\n      accessToken: trimmed,\n      refreshToken: null,\n      profileArn: null,\n      region,\n      authMethod: \"api_key\",\n    };\n  }\n","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/services/kiro.js#L315-L351","documentation":"validateApiKey is a synchronous guard: before making any network call it requires a non-empty, non-whitespace string apiKey. If apiKey is undefined, null, not a string, or only whitespace, it throws 'API key is required' immediately. This is a fail-fast precondition so an obviously invalid credential never reaches the Amazon Q endpoint.","triggerScenarios":"Calling kiroService.validateApiKey(undefined), validateApiKey(null), validateApiKey(42), validateApiKey(\"   \"), or validateApiKey(\"\") — i.e. any call where the first argument fails `apiKey && typeof apiKey === 'string' && apiKey.trim()`.","commonSituations":"Reading the key from an unset env var (process.env.KIRO_API_KEY === undefined); a config object whose key field is misspelled (apiKey vs api_key); trimming stripping a whitespace-only placeholder; passing a credentials object instead of the string itself.","solutions":["Pass the API key string as the first argument, e.g. validateApiKey(process.env.KIRO_API_KEY).","Check the credential source: confirm the env var / config field is set and correctly named before calling.","Trim the value in your own code and reject empty strings early with a clearer app-level message.","If passing from a credentials object, use the right property: cred.apiKey, not cred."],"exampleFix":"// before\nawait kiro.validateApiKey(config.kiro_key); // field name typo -> undefined\n// after\nif (!process.env.KIRO_API_KEY) throw new Error(\"Set KIRO_API_KEY first\");\nawait kiro.validateApiKey(process.env.KIRO_API_KEY);","handlingStrategy":"validation","validationCode":"function requireApiKey(key) {\n  if (!key || typeof key !== \"string\" || !key.trim()) {\n    throw new Error(\"Kiro API key is missing or empty\");\n  }\n  return key.trim();\n}\nawait kiro.validateApiKey(requireApiKey(process.env.KIRO_API_KEY));","typeGuard":"function isApiKey(value) {\n  return typeof value === \"string\" && value.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Validate the key exists at config/startup time, not at first API call.","Use a config loader that fails fast on missing credential fields.","Remember the argument is the key string itself, not a credentials object.","Trim user-pasted keys and reject whitespace-only input in your UI/CLI."],"tags":["validation","missing-argument","api-key","kiro","oauth"],"backgroundTag":"missing-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}