{"record":{"id":"9ea83539e5310fe6","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":null,"httpStatus":null,"severity":"error","filePath":"packages/provider-inference/src/providers/cloud/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/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/provider-inference/src/providers/cloud/google-gemini-audio-speech/index.ts#L62-L98","documentation":"After parsing the JSON body, the Gemini TTS wrapper validates that the parsed body.input field contains text to synthesize. This error means the speech request reached the fetch wrapper with no input text. The library throws early instead of sending a doomed request to the Gemini API.","triggerScenarios":"Invoking the speech API with an empty string or missing 'input'/'text' option, e.g. speech({ text: '' }) or a caller that computed the text to speak as undefined/empty.","commonSituations":"A pipeline upstream produced empty text (empty LLM output, stripped string), a UI sent the request before the user typed text, or the option was renamed in a refactor so the value lands in a different key.","solutions":["Pass non-empty text in the speech request options","Trim and check the text before calling speech(); skip the call when empty","If text comes from upstream (LLM, user input), guard that producer against empty output","Verify the option key name matches the current provider API (renamed fields silently produce undefined)"],"exampleFix":"// before\nawait speech({ input: text })\n// after\nif (!text?.trim()) return\nawait speech({ input: text })","handlingStrategy":"validation","validationCode":"const text = options.input?.trim()\nif (!text) throw new Error('Refusing to call Gemini TTS with empty input text')","typeGuard":"function hasInput(opts: { input?: string }): opts is { input: string } {\n  return typeof opts.input === 'string' && opts.input.trim().length > 0\n}","tryCatchPattern":"try {\n  const audio = await speech({ input: text, model })\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Missing input text')) {\n    // skip synthesis or prompt the user for text\n  }\n}","preventionTips":["Trim and check text before every TTS call","Guard upstream producers (LLM output, user input) against empty strings","Log the request options before sending to catch silently-dropped fields"],"tags":["tts","validation","empty-input"],"backgroundTag":"empty-required-field","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"}