{"record":{"id":"76d83f2dd3410054","repo":"decolua/9router","slug":"no-gemini-api-key-configured","errorCode":null,"errorMessage":"No Gemini API key configured","messagePattern":"No Gemini API key configured","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/handlers/ttsProviders/gemini.js","lineNumber":63,"sourceCode":"  header.writeUInt16LE(CHANNELS, 22);\n  header.writeUInt32LE(SAMPLE_RATE, 24);\n  header.writeUInt32LE(byteRate, 28);\n  header.writeUInt16LE(blockAlign, 32);\n  header.writeUInt16LE(BITS_PER_SAMPLE, 34);\n  header.write(\"data\", 36);\n  header.writeUInt32LE(dataSize, 40);\n  return Buffer.concat([header, pcmBuffer]);\n}\n\n// Build TTS prompt: add \"Say [in {language}]:\" prefix to force TTS mode\nfunction buildPrompt(text, language) {\n  if (/:\\s/.test(text)) return text; // user already provided style instruction\n  return language ? `Say in ${language}: ${text}` : `Say: ${text}`;\n}\n\nexport default {\n  async synthesize(text, model, credentials, _responseFormat, opts = {}) {\n    if (!credentials?.apiKey) throw new Error(\"No Gemini API key configured\");\n    const { modelId, voiceId } = parseGeminiModelVoice(model);\n    const url = `${TTS_BASE}/${modelId}:generateContent?key=${credentials.apiKey}`;\n    const res = await fetch(url, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({\n        contents: [{ parts: [{ text: buildPrompt(text, opts.language) }] }],\n        generationConfig: {\n          responseModalities: [\"AUDIO\"],\n          speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: voiceId } } },\n        },\n      }),\n    });\n    if (!res.ok) {\n      const err = await res.json().catch(() => ({}));\n      throw new Error(err?.error?.message || `Gemini TTS failed: ${res.status}`);\n    }\n    const data = await res.json();","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/ttsProviders/gemini.js#L45-L81","documentation":"The Gemini TTS provider's synthesize throws \"No Gemini API key configured\" when `credentials?.apiKey` is missing. Gemini TTS uses generateContent with AUDIO response modality and authenticates via the `key` query parameter, so without an API key the request cannot even be built correctly.","triggerScenarios":"Routing a TTS request to the Gemini provider while no Gemini API key is configured: provider enabled in 9Router without credentials, key stored under a field other than `apiKey`, key removed later, or synthesize called directly with null credentials.","commonSituations":"Fresh setup where the Gemini API key was never entered in the dashboard; using an OAuth-based Gemini credential (which this provider does not read — it needs `apiKey`); key expired/deleted in Google AI Studio; env var set but not wired into the provider credentials object.","solutions":["Create an API key in Google AI Studio and enter it as the Gemini provider's API key in the 9Router dashboard.","Ensure the key is stored under `credentials.apiKey` — the exact field this guard checks.","Verify the key works: `curl \"https://generativelanguage.googleapis.com/v1beta/models?key=$KEY\"` should list models.","If you set GEMINI_API_KEY / GOOGLE_API_KEY in env, confirm it is actually passed into the provider credentials object rather than just exported."],"exampleFix":"// before\nawait gemini.synthesize(text, \"gemini-2.5-flash-preview-tts/Kore\", {}); // no apiKey\n// after\nconst creds = { apiKey: process.env.GEMINI_API_KEY };\nif (!creds.apiKey) throw new Error(\"Set GEMINI_API_KEY for Gemini TTS\");\nawait gemini.synthesize(text, \"gemini-2.5-flash-preview-tts/Kore\", creds);","handlingStrategy":"validation","validationCode":"function requireGeminiCreds(creds) {\n  if (typeof creds?.apiKey !== \"string\" || !creds.apiKey.trim()) {\n    throw new Error(\"Gemini TTS needs an API key (Google AI Studio) configured as credentials.apiKey\");\n  }\n  return { apiKey: creds.apiKey.trim() };\n}","typeGuard":"function hasGeminiKey(creds) {\n  return typeof creds === \"object\" && creds !== null && typeof creds.apiKey === \"string\" && creds.apiKey.trim() !== \"\";\n}","tryCatchPattern":"try {\n  const audio = await gemini.synthesize(text, model, creds);\n} catch (e) {\n  if (e.message === \"No Gemini API key configured\") {\n    return res.status(400).json({ error: \"Add a Gemini API key in the dashboard before using Gemini TTS\" });\n  }\n  throw e;\n}","preventionTips":["Create the key in Google AI Studio and store it under the Gemini provider's `apiKey`.","Note this provider needs an API key, not OAuth credentials.","Add a startup check that validates the key against the models list endpoint.","Fail fast at config load when Gemini TTS is enabled without a key."],"tags":["authentication","missing-credentials","tts","api-key"],"backgroundTag":"missing-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}