{"record":{"id":"6b361aadf0b009ee","repo":"moeru-ai/airi","slug":"missing-input-text-for-gemini-tts","errorCode":null,"errorMessage":"Missing input text for Gemini TTS","messagePattern":"Missing input text for Gemini TTS","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/libs/providers/providers/google-gemini-audio-speech/index.ts","lineNumber":80,"sourceCode":"  const bytes = new Uint8Array(binary.length)\n  for (let index = 0; index < binary.length; index++)\n    bytes[index] = binary.charCodeAt(index)\n  return bytes\n}\n\nfunction createAudioFetch(apiKey: string, baseUrl: string) {\n  return async (_input: RequestInfo | URL, init?: RequestInit) => {\n    if (!init?.body || typeof init.body !== 'string')\n      throw new Error('Invalid request body')\n\n    const body = JSON.parse(init.body) as {\n      input?: string\n      model?: string\n      voice?: string\n      temperature?: number\n    }\n    if (!body.input)\n      throw new Error('Missing input text for Gemini TTS')\n    if (!body.model)\n      throw new Error('Missing model for Gemini TTS')\n\n    const response = await globalThis.fetch(new URL(`models/${body.model}:generateContent`, baseUrl), {\n      method: 'POST',\n      headers: { 'x-goog-api-key': apiKey, 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        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)","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/stage-ui/src/libs/providers/providers/google-gemini-audio-speech/index.ts#L62-L98","documentation":"Thrown inside the Gemini TTS custom fetch wrapper when the request body parses to an object with no `input` field. The wrapper builds the Gemini generateContent request from a JSON body contract (input/model/voice/temperature); missing input text means the caller never supplied the text to synthesize, so the request is aborted before hitting the network.","triggerScenarios":"Calling the provider's speech fetch with a JSON string body where the `input` key is missing, empty-string, or undefined. The upstream caller (TTS module) constructed the body without a text field, or passed a differently-shaped object.","commonSituations":"TTS invoked with empty text (e.g. an empty assistant message). Caller bug building the body with a wrong key name (e.g. `text` instead of `input`). Template/coder error stripping the field.","solutions":["Ensure the caller passes a non-empty `input` field in the request body JSON.","Guard upstream: skip TTS when the text is empty or whitespace.","Verify the body key name matches `input` exactly (not `text`/`prompt`)."],"exampleFix":"// before\nfetch(url, { body: JSON.stringify({ model: 'gemini-2.5-flash-preview-tts', voice: 'Kore' }) })\n// after\nfetch(url, { body: JSON.stringify({ input: 'Hello world', model: 'gemini-2.5-flash-preview-tts', voice: 'Kore' }) })","handlingStrategy":"validation","validationCode":"function hasGeminiInput(body: unknown): boolean {\n  return !!body && typeof body === 'object'\n    && typeof (body as { input?: unknown }).input === 'string'\n    && (body as { input: string }).input.length > 0\n}\n// before fetch:\nif (!hasGeminiInput(parsedBody)) {\n  // do not call fetch; skip TTS for empty text\n}","typeGuard":"function isGeminiTtsBody(body: unknown): body is { input: string, model: string, voice?: string, temperature?: number } {\n  return !!body && typeof body === 'object'\n    && typeof (body as { input?: unknown }).input === 'string'\n    && (body as { input: string }).input.length > 0\n    && typeof (body as { model?: unknown }).model === 'string'\n}","tryCatchPattern":"try {\n  await provider.speech(model).fetch(url, { body: JSON.stringify(body) })\n}\ncatch (err) {\n  if (err instanceof Error && err.message === 'Missing input text for Gemini TTS') {\n    // caller bug: ensure `input` is set; skip empty text\n  }\n  else throw err\n}","preventionTips":["Never invoke TTS with empty/whitespace text.","Use the exact `input` key in the request body.","Validate the body shape at the caller before dispatching to the provider fetch."],"tags":["validation","gemini","tts","precondition","request-body"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}