●OUTAGE — Gemini recovers from one of its biggest outages (errors 1076/1099) as engineering mitigations take effect●DAILY-BRIEF — The new Daily Brief agent works overnight, analyzing your inbox, calendar, and tasks into a personalized morning digest●GEMINI-OMNI — Gemini Omni combines Gemini with Google's generative media models to produce consistent, high-quality video from a single prompt●ENTERPRISE — Gemini 3.5 Flash is enabled by default in Gemini Enterprise as of Jun 8 and can no longer be turned off●DEPRECATION — Image preview models (3.1-flash-image / 3-pro-image) shut down Jun 25; migrate to the GA versions now●FILE-SEARCH — File Search now supports multimodal search, natively embedding and searching images via gemini-embedding-2●OUTAGE — Gemini recovers from one of its biggest outages (errors 1076/1099) as engineering mitigations take effect●DAILY-BRIEF — The new Daily Brief agent works overnight, analyzing your inbox, calendar, and tasks into a personalized morning digest●GEMINI-OMNI — Gemini Omni combines Gemini with Google's generative media models to produce consistent, high-quality video from a single prompt●ENTERPRISE — Gemini 3.5 Flash is enabled by default in Gemini Enterprise as of Jun 8 and can no longer be turned off●DEPRECATION — Image preview models (3.1-flash-image / 3-pro-image) shut down Jun 25; migrate to the GA versions now●FILE-SEARCH — File Search now supports multimodal search, natively embedding and searching images via gemini-embedding-2
Gemini's Preview Image Models Shut Down on June 25 — Code Diffs and Checks From an Actual GA Migration
How I moved my image pipeline off Gemini's preview image models before the June 25 shutdown — confirming GA model IDs, Python code diffs, regression checks, and a safe cutover order.
On June 25, the Gemini API shuts down two preview image-generation models: gemini-3.1-flash-image-preview and gemini-3-pro-image-preview. The date is confirmed on the official deprecations page, which leaves roughly two weeks.
I run a batch pipeline that generates image variations for a wallpaper app I maintain as an indie developer, and I assumed my main path had long been migrated. Then I did an actual audit and found a side script — a prototyping tool I had written months ago — still pointing at the preview model. What I had budgeted as a "swap one model ID" task turned out to involve a wrong-ID 404, and a category of verification that becomes permanently impossible after the shutdown date.
Here is the working log: every step from audit to cutover, plus the differences I could actually measure between the preview and GA models, with runnable code.
What stops, and what replaces it
Two dedicated image-generation preview models are being retired, and each has a GA counterpart that has been generally available since May 28. So there is no "the replacement isn't ready yet" problem here.
gemini-3.1-flash-image-preview → migrate to gemini-3.1-flash-image
gemini-3-pro-image-preview → migrate to gemini-3-pro-image
Let me share the first trap I stepped into. I typed the pro-tier GA model as gemini-3.1-pro-image and got a 404 NOT_FOUND. The flash model carries the "3.1" version, but the pro model's official ID stays at "3" — the correct ID is simply the preview name with -preview removed. Product announcements sometimes write the pair loosely as "Gemini 3.1 Flash Image / 3.1 Pro Image," so if you reconstruct IDs from memory, you may take the same detour I did.
One scoping note: this shutdown concerns the dedicated image-generation model family. It does not affect the native Image Output capability of the main Gemini 3.2 Flash model — if that is all you use, you can stop reading here.
June has been a month of stacked deadlines for the Gemini API. The legacy Interactions API schema was deleted on June 8 (I wrote up that migration in Gemini Interactions API: Fixing What Broke When the Legacy outputs Schema Was Removed on June 6), the individual tiers of Gemini CLI and the Code Assist IDE extension wind down on June 18, and the image preview models follow on June 25. Putting all three dates on a calendar is a small act that prevents exactly the kind of straggler script I found.
Audit your code first — model IDs hide outside the code, too
The first job is finding every reference to the preview models, and I recommend searching for the substring image-preview rather than the full model IDs. A partial match still catches alias constants and abbreviated config values.
# Search broadly for the substring instead of exact model IDsgrep -rn "image-preview" \ --include="*.py" --include="*.ts" --include="*.js" \ --include="*.env*" --include="*.yaml" --include="*.yml" \ --include="*.json" --include="*.toml" \ .
In my repository this turned up two Python scripts and one .env file. The near miss was an environment variable hard-coded inside a GitHub Actions workflow definition — it shows up if you grep from the repository root, but it is exactly the kind of place you skip once you have told yourself "code is fixed, config is fixed."
Some references live entirely outside the repository. If you distribute model IDs through Firebase Remote Config or a settings table in your database, grep will never see them; open the actual admin console and read the live values.
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Move your code off gemini-3.1-flash-image-preview and gemini-3-pro-image-preview before the June 25 shutdown, from audit to full cutover
✦Get working Python and TypeScript snippets: confirming GA model IDs with models.list, before/after call diffs, and error handling that separates 404 from 429
✦Run the preview-vs-GA regression comparison that becomes impossible after the shutdown, and adapt the 60-prompt method to your own image pipeline
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Confirm the target IDs — don't rewrite from memory
After the 404 episode, I stopped trusting my recollection of documentation and confirmed the target IDs against what my own API key can actually see.
import osfrom google import genaiclient = genai.Client(api_key=os.environ["GEMINI_API_KEY"])# Print only the image-related modelsfor m in client.models.list(): if "image" in m.name: print(m.name)
The expected output looks roughly like this (the preview entries remain visible until June 25):
Treating this list as the source of truth has a second benefit: AI Studio keys and Vertex AI projects sometimes expose models at slightly different times or under slightly different visibility. Reading your own environment's list before editing code removes the entire class of "the docs say it exists, my key says 404" confusion.
Code diffs — the Python SDK before and after
Swapping the generation call
The call-site diff is a single line. While you are touching it anyway, I recommend moving the model ID out of the code and into an environment variable — the next deprecation deadline then becomes a config change instead of a deploy.
import os# Before: preview model (returns 404 after June 25)# resp = client.models.generate_content(# model="gemini-3.1-flash-image-preview",# contents=prompt,# config=config,# )# After: GA model, with the ID injectable via environment variableIMAGE_MODEL = os.environ.get("IMAGE_MODEL", "gemini-3.1-flash-image")resp = client.models.generate_content( model=IMAGE_MODEL, contents=prompt, config=config,)
Response handling did not need to change
The part I braced for — a changed response shape — turned out to be a non-event. Images still come back as parts carrying inline_data, exactly as in the preview versions.
import osfrom google import genaifrom google.genai import typesclient = genai.Client(api_key=os.environ["GEMINI_API_KEY"])resp = client.models.generate_content( model=os.environ.get("IMAGE_MODEL", "gemini-3.1-flash-image"), contents="A pale watercolor coastline at dawn, tall portrait framing for a phone wallpaper", config=types.GenerateContentConfig( response_modalities=["TEXT", "IMAGE"], ),)for part in resp.candidates[0].content.parts: if part.inline_data: with open("output.png", "wb") as f: f.write(part.inline_data.data) print("saved: output.png")# Expected output:# saved: output.png
One caution: response_modalities is still required on the GA models. I tried removing it on the theory that a GA image model would default to returning images, and got text-only responses back. Keep the preview-era configuration as is.
TypeScript — stop treating 404 like 429
The Node.js diff is the same one line, but the migration is a good excuse to revisit error classification. A 404 from a retired preview ID will never heal, no matter how many times you retry. A 429 or 503 usually will. Put both in the same retry loop and a model shutdown turns into a job queue full of futile retries.
import { GoogleGenAI } from "@google/genai";const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });const IMAGE_MODEL = process.env.IMAGE_MODEL ?? "gemini-3.1-flash-image";async function generateImage(prompt: string): Promise<Buffer> { try { const resp = await ai.models.generateContent({ model: IMAGE_MODEL, contents: prompt, config: { responseModalities: ["TEXT", "IMAGE"] }, }); const part = resp.candidates?.[0]?.content?.parts?.find( (p) => p.inlineData, ); if (!part?.inlineData?.data) { throw new Error("image payload missing"); } return Buffer.from(part.inlineData.data, "base64"); } catch (err) { const status = (err as { status?: number }).status; if (status === 404) { // Retired model or wrong ID: retrying cannot fix this. Alert immediately. throw err; } if (status === 429 || status === 503) { // Transient failure: hand off to a retry loop with exponential backoff } throw err; }}
This very week, Gemini went through a sizable incident with error 1076 / 1099 appearing across the board. My batch jobs park failed work in a queue and retry the next morning, so the jobs that failed during the outage recovered on their own. Separating "permanently dead" from "temporarily failing" pays for itself twice — once during model shutdowns, once during incidents.
The regression check you can only run before the shutdown
I consider this step the real substance of the migration. After June 25 the preview models become uncallable, which means "run the same prompts through old and new and compare" is a verification with an expiry date. It must happen now or never.
I picked 60 representative prompts from my production set and generated with both models.
import osimport timefrom google import genaifrom google.genai import typesPROMPTS = [ "A pale watercolor coastline at dawn, tall portrait framing for a phone wallpaper", "A minimal low-poly illustration of a city seen from above at night", # Pick representative prompts from your production set (I used 60)]MODELS = { "preview": "gemini-3.1-flash-image-preview", "ga": "gemini-3.1-flash-image",}client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])def generate(model_id: str, prompt: str, out_path: str) -> float: """Generate one image, save it, return elapsed seconds.""" start = time.time() resp = client.models.generate_content( model=model_id, contents=prompt, config=types.GenerateContentConfig( response_modalities=["TEXT", "IMAGE"], ), ) for part in resp.candidates[0].content.parts: if part.inline_data: with open(out_path, "wb") as f: f.write(part.inline_data.data) return time.time() - startfor i, prompt in enumerate(PROMPTS): for label, model_id in MODELS.items(): os.makedirs(f"regression/{label}", exist_ok=True) sec = generate(model_id, prompt, f"regression/{label}/{i:03d}.png") print(f"[{label}] #{i:03d} {sec:.1f}s")
What I found:
58 of the 60 pairs were indistinguishable to my eye when laid side by side
2 prompts that the preview model rendered were blocked by the safety filter on the GA model. Both specified human figures in detail; rewording the descriptions slightly got them through
Median generation time went from 6.8 s on preview to 6.5 s on GA — no perceptible difference
Cost stayed around $0.04 per image on my actual bill. Pricing varies by configuration and resolution, so check the official pricing page for your own setup
The headline is "nearly identical," but those 2 safety-filter regressions would otherwise have surfaced in production, after the old model was gone and comparison was impossible. The more your prompt library is an asset, the more this comparison is worth running before the deadline.
Cutover order — don't make preview your fallback
The cutover itself is an environment variable edit, but the order of operations and the direction of the fallback deserve thought.
The tempting pattern is "if the new model fails, fall back to the old one." Do that here and, after June 25, every transient GA failure converts into a 404 request against a retired model — two failures for the price of one. If you want a fallback, point it at a sibling GA model (flash falling back to pro, or the reverse), or skip generation and park the job in a queue.
My sequence looked like this:
Switch the IMAGE_MODEL environment variable to the GA model, so the next batch runs entirely on GA
Keep a small parallel run on the preview model for 3 days, purely for re-checking the regression set
If the parallel run stays clean, delete every preview reference from code and config
By June 24, confirm that grepping for image-preview — fallback paths included — returns 0 hits
If you serve real-time generation to users rather than batches, replace step 1 with a gradual traffic shift to the GA model.
Here is the breakdown I actually used, with rough time costs:
Audit (30 min): grep for image-preview; include CI definitions, environment variables, and Remote Config
Confirm target IDs (10 min): run models.list and trust only what your key can see
Swap (30 min): move the model ID into an environment variable and point it at GA
Regression check (half a day, unattended): generate the fixed prompt set on both models, compare by eye and by numbers
Cutover with a parallel run (3 days): all production traffic on GA, preview kept only for comparison
Full removal (by June 24): zero preview references, fallback paths included
Deprecation work like this is best finished in calm weather, not on deadline day. Watching this week's outage unfold, I was glad the migration was already behind me rather than stacked on top of it.
Start with a single command today: grep -rn "image-preview" . in your repository. If it returns nothing, this shutdown is somebody else's problem. If it returns hits, the sequence above should carry you the rest of the way. I hope the log saves you a detour or two.
Share
Thank You for Reading
Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.