Google's new Gemini 3.8 TTS models hand back WAV audio with a RIFF header on ordinary requests, while streaming still returns raw PCM. Code written for older Gemini TTS models that adds its own WAV header will now produce a doubled header. Here is how to handle both paths cleanly.
If you built a text to speech pipeline on earlier Gemini TTS models, there is a good chance your code contains a small helper that takes raw audio bytes and wraps them in a WAV header before saving or playing them. That helper was necessary. It is now a bug waiting to happen if you point it at the new models.
Google's Gemini API documentation says the Gemini 3.8 TTS models return WAV audio with a standard RIFF header by default for unary requests, unlike earlier TTS models such as gemini-3.1-flash-tts-preview, which returned headerless raw PCM in the audio/l16 format. The model IDs are gemini-3.8-flash-tts and gemini-3.8-flash-lite-tts. Swap either one into old code that still runs the wrapping helper and you get a file with a header sitting in front of another header, which some players tolerate, some reject, and some play with a click or burst of noise at the start.
Streaming behaves differently, and that is the second trap. The speech generation documentation says streaming requests return headerless raw 16 bit linear PCM chunks in the audio/l16 format, at 24 kHz mono, by default. So the same application can receive a complete WAV file on one code path and raw PCM on the other. If your voice agent streams audio to a phone line but saves transcripts of calls as unary requests, you now need two handling rules instead of one.
You do not have to accept the defaults. The documentation says you can set the output format inside response_format, using a mime_type such as audio/l16 and an optional sample_rate, for example 24000, 16000 or 8000 Hz. Telephony systems commonly expect 8000 Hz audio, so asking for it directly saves a resampling step you would otherwise run yourself.
The cleanest fix is to make the format explicit in every request rather than relying on whatever the model defaults to. Defaults change between model generations, as this one just did, and explicit settings make your pipeline behave the same way when you upgrade next time.
Try it today
- Search your codebase for any helper that adds a WAV or RIFF header to Gemini TTS output, and gate it on the model ID so it only runs for pre 3.8 models.
- For unary calls to gemini-3.8-flash-tts or gemini-3.8-flash-lite-tts, save the returned bytes directly as a .wav file with no extra wrapping.
- For streaming calls, keep treating chunks as raw audio/l16 PCM at 24 kHz mono and add a header only when you write the final file.
- Set response_format explicitly, with mime_type audio/l16 and a sample_rate of 8000 for telephony, so upgrades cannot silently change your output.