{"record":{"id":"58c8d69dfbb48834","repo":"moeru-ai/airi","slug":"gemini-tts-response-missing-audio-data-58c8d6","errorCode":null,"errorMessage":"Gemini TTS response missing audio data","messagePattern":"Gemini TTS response missing audio data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/provider-inference/src/providers/cloud/google-gemini-audio-speech/index.ts","lineNumber":106,"sourceCode":"        contents: [{ parts: [{ text: body.input }] }],\n        generationConfig: {\n          responseModalities: ['AUDIO'],\n          speechConfig: {\n            voiceConfig: { prebuiltVoiceConfig: { voiceName: body.voice || 'Kore' } },\n          },\n          ...(body.temperature !== undefined ? { temperature: body.temperature } : {}),\n        },\n      }),\n    })\n    if (!response.ok)\n      throw new Error(`Gemini TTS request failed: ${response.status} ${await response.text().catch(() => '')}`)\n\n    const data = await response.json() as {\n      candidates?: Array<{ content?: { parts?: Array<{ inlineData?: { data?: string } }> } }>\n    }\n    const audio = data.candidates?.[0]?.content?.parts?.find(part => part.inlineData)?.inlineData?.data\n    if (!audio)\n      throw new Error('Gemini TTS response missing audio data')\n\n    return new Response(toWavFromPCM16(decodeBase64(audio), 24000), {\n      status: 200,\n      headers: { 'Content-Type': 'audio/wav' },\n    })\n  }\n}\n\nexport const providerGoogleGeminiAudioSpeech = defineProvider<GoogleGeminiSpeechConfig, 'google-gemini-audio-speech'>({\n  id: 'google-gemini-audio-speech',\n  name: 'Google Gemini',\n  nameLocalize: ({ t }) => t('settings.pages.providers.provider.google-gemini-audio-speech.title'),\n  description: 'aistudio.google.com',\n  descriptionLocalize: ({ t }) => t('settings.pages.providers.provider.google-gemini-audio-speech.description'),\n  tasks: ['text-to-speech', 'tts'],\n  icon: 'i-lobe-icons:gemini',\n  iconColor: 'i-lobe-icons:gemini-color',\n  createProviderConfig: () => googleGeminiSpeechConfigSchema,","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/provider-inference/src/providers/cloud/google-gemini-audio-speech/index.ts#L88-L124","documentation":"The Gemini API responded 200 but the response JSON contained no inlineData audio in candidates[0].content.parts. Gemini can return successful HTTP responses without audio when generation is blocked or empty (safety filters, empty input, prompt feedback).","triggerScenarios":"The API returned a candidates array with no parts carrying inlineData: blocked by safety settings, empty result for the given text, or a response shape change in the preview API.","commonSituations":"Input text triggers safety filtering, using a non-TTS Gemini model that returns text-only parts, or Google changing the preview response schema.","solutions":["Log the full response JSON (before this point) to inspect candidates, finishReason, and promptFeedback","Check finishReason for SAFETY/blocking and adjust input text or safety settings","Verify the model id is actually a TTS model that returns inline audio data","Handle the empty-audio case in caller code and surface a user-facing message instead of failing silently"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// no client-side check can detect this; validate input text is non-empty to reduce risk\nif (!input?.trim()) return null","typeGuard":"function hasAudio(data: unknown): data is { candidates: Array<{ content: { parts: Array<{ inlineData: { data: string } }> } }> } {\n  const d = data as any\n  return Boolean(d?.candidates?.[0]?.content?.parts?.some((p: any) => p?.inlineData?.data))\n}","tryCatchPattern":"try {\n  const audio = await speech({ input, model })\n} catch (e) {\n  if (e instanceof Error && e.message === 'Gemini TTS response missing audio data') {\n    // inspect finishReason/safety feedback, adjust input, or inform the user\n  }\n}","preventionTips":["Avoid input text likely to trip safety filters","Check finishReason and promptFeedback when synthesis comes back empty","Pin and track the preview TTS API for schema changes","Treat empty audio as a distinct UI state, not a generic crash"],"tags":["tts","empty-response","gemini"],"backgroundTag":"empty-api-response","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-09-08T13:18:15.005Z","contentChangedAt":"2026-09-08T13:18:15.005Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}