On Sunday morning I reopened the deprecations page and my eyes stopped halfway down the table. In the version updated on October 22, November 17 appeared in a whole cluster of rows: three TTS models and two Live models, every one with preview in its name.
The page calls those dates the earliest possible shutdown dates. Nothing stops sooner, but nothing promises it slips later either. That leaves 23 days from today. If you have ever added voice output to a side project, can you say right now which file holds those names? As an indie developer I couldn't, which is why I wrote a counter before touching anything.
What I'd say first: finish counting the names before you replace any of them. Once you have a number, the order of work and whether it fits into this week both become visible.
The names affected
The October 22 table lists these five with a November 17 date:
gemini-3.1-flash-tts-preview, withgemini-3.8-flash-ttsorgemini-3.8-flash-lite-ttsas the suggested replacementgemini-2.5-flash-preview-tts, same replacementgemini-2.5-pro-preview-tts, same replacementgemini-3.1-flash-live-preview, replaced bygemini-3.8-livegemini-2.5-flash-native-audio-preview-12-2025, also replaced bygemini-3.8-live
The same page also shows the Omni models that stopped on October 22 and the old Deep Research agent that stopped on October 23. Those are already behind us, so I leave them out. For how I inventoried an earlier deadline, see the September 30 Omni preview inventory.
A script that counts the names
Searching by hand, I would forget to open .env.local or some YAML file. So this script walks the whole folder and prints file and line number, nothing more.
#!/usr/bin/env python3
"""List which file and line still names a model that is scheduled to stop."""
import re
import sys
from pathlib import Path
# Copied from the deprecations page updated 2026-10-22 (dates are "earliest possible")
RETIRING = {
"gemini-3.1-flash-tts-preview": ("2026-11-17", "gemini-3.8-flash-tts / gemini-3.8-flash-lite-tts"),
"gemini-2.5-flash-preview-tts": ("2026-11-17", "gemini-3.8-flash-tts / gemini-3.8-flash-lite-tts"),
"gemini-2.5-pro-preview-tts": ("2026-11-17", "gemini-3.8-flash-tts / gemini-3.8-flash-lite-tts"),
"gemini-3.1-flash-live-preview": ("2026-11-17", "gemini-3.8-live"),
"gemini-2.5-flash-native-audio-preview-12-2025": ("2026-11-17", "gemini-3.8-live"),
}
SKIP_DIRS = {".git", "node_modules", ".next", "dist", "build", "__pycache__", ".venv"}
TEXT_EXT = {".py", ".js", ".ts", ".tsx", ".json", ".yaml", ".yml", ".toml",
".env", ".md", ".txt", ".sh", ".swift", ".kt", ".dart", ".cfg", ".ini"}
def candidates(root: Path):
for p in root.rglob("*"):
if any(part in SKIP_DIRS for part in p.parts) or not p.is_file():
continue
# Names like .env.local have a different suffix, so check the prefix too
if p.suffix in TEXT_EXT or p.name.startswith(".env"):
yield p
def main(root_arg: str) -> int:
root = Path(root_arg)
# Longest names first so a long name is never matched as a shorter fragment
pattern = re.compile("|".join(re.escape(k) for k in sorted(RETIRING, key=len, reverse=True)))
hits = 0
for path in candidates(root):
try:
lines = path.read_text(encoding="utf-8").splitlines()
except (UnicodeDecodeError, OSError):
continue # skip binaries and unreadable files
for no, line in enumerate(lines, 1):
for m in pattern.finditer(line):
date, to = RETIRING[m.group(0)]
print(f"{path.relative_to(root)}:{no}\t{m.group(0)}\t{date}\t-> {to}")
hits += 1
print(f"-- {hits} hits", file=sys.stderr)
return 1 if hits else 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1] if len(sys.argv) > 1 else "."))It exits with 1 when anything is found, so you can drop it into a shell script or CI later.
What I confirmed by running it
I made a throwaway folder with three files (.env.local, one Python file, one YAML file) and planted four retiring names. All four came back:
.env.local:1 gemini-2.5-flash-preview-tts 2026-11-17 -> gemini-3.8-flash-tts / gemini-3.8-flash-lite-tts
src/voice.py:1 gemini-2.5-flash-native-audio-preview-12-2025 2026-11-17 -> gemini-3.8-live
config/app.yaml:1 gemini-3.1-flash-tts-preview 2026-11-17 -> gemini-3.8-flash-tts / gemini-3.8-flash-lite-tts
config/app.yaml:2 gemini-2.5-pro-preview-tts 2026-11-17 -> gemini-3.8-flash-tts / gemini-3.8-flash-lite-ttsThe second YAML hit sits inside a comment. I want that one, because an outdated name left in a note tends to get copied by the next person.
Three things I watched for while writing it
Filtering by extension alone drops .env.local. Path.suffix returns .local there, so the .env entry never matches. The script checks whether the filename starts with .env as a separate condition.
Match longest names first. A long name such as gemini-2.5-flash-native-audio-preview-12-2025 can overlap shorter ones, and without ordering you may get a truncated match.
It cannot see everything. Model names built from pieces like "gemini-" + version + "-flash-tts", settings saved in a database or a cloud console, and the inside of no-code tools are all invisible to a text search. Read "0 hits" as "0 in this folder", never as "0 anywhere".
Fixing what it finds
I order fixes by how bad it is if the name stops, not by how easy the swap is:
- Settings the production server reads (environment variables, config files)
- Constants bundled into the app
- Tests, samples, and READMEs
The direction is TTS to TTS and Live to Live, as the table says. Renaming alone does not guarantee the same voice quality or response timing, though. For gemini-3.8-live, the session thinking in designing sessions for a Live API interpreter app carries over directly. I keep listening tests as a separate step after the rename, which makes problems easier to isolate.
After the swap, I also log the model name that comes back in the response, so an automatic reroute does not slip past. The method is in what to check the morning Flash answers differently.
What to do today
Count today, fix after counting. Keeping just that line lets you judge whether 23 days is long or short using the number in your own folder.
Run the script once on your largest project and write the count in a note. If it returns zero, open one saved setting in a console and check it for the same names before you relax.