After reading through the August deprecation cluster in the changelog, I ran my local test suite. Six tests, all green, 0.11 seconds. Under that green sat three recorded responses from a model that stops serving on August 17.
In my solo app project, where I have been evaluating an image generation pipeline, every Gemini API test replays a recorded response — a cassette — instead of hitting the network. That is a reasonable design; nobody wants CI billing them per run. But for one specific failure class, model retirement, the same design actively works against you.
A passing suite and a production system that still works next week are two different claims. I spent a day wiring them back together, and measured what it cost.
What stops in August, and why mocks are structurally blind to it
First, the context. August 2026 is unusually dense with shutdowns.
| What | Date | Impact |
|---|---|---|
Imagen 4 family (imagen-4.0-generate-001 / ultra / fast) and the Gemini 3 Image family | Retires 2026-08-17 | Image generation pipelines must migrate |
| Grok 4.1 family on Gemini Enterprise Agent Platform | Retires 2026-08-20 | Agent configs need replacement |
gemini-robotics-er-1.6-preview | Retires 2026-08-31 | Preview-dependent paths need a fallback |
| 3.5 Flash in Gemini Enterprise (global region) | Removed 2026-08-04 | The model silently leaves the picker |
Dates and scope can shift, so verify against the model deprecation schedule before acting on any of this.
Now consider how most of us test API integrations. Recorded replays, SDK mocks, stub servers — different names for the same move: freezing whatever is on the other side of the network. Latency jitter disappears from your tests. So do rate limits. And so does a model ceasing to exist.
Model retirement fits precisely into the class of failures mocks are designed to hide. The more thoroughly you mock, the later you find out. That inverse relationship is the uncomfortable core of this article.
Reproducing the green-but-dead state in a minimal setup
A working example beats an abstract warning. Here is a minimal client, modeled on a wallpaper app's generation path.
# client.py
import os
import requests
BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
class GeminiImageClient:
def __init__(self, model: str, api_key: str | None = None, base_url: str = BASE_URL):
self.model = model
self.api_key = api_key or os.environ.get("GEMINI_API_KEY", "")
self.base_url = base_url # injection point: tests aim this at a stub
def generate(self, prompt: str, aspect_ratio: str = "9:16") -> dict:
url = f"{self.base_url}/models/{self.model}:predict"
resp = requests.post(
url,
params={"key": self.api_key},
json={"instances": [{"prompt": prompt}],
"parameters": {"sampleCount": 1, "aspectRatio": aspect_ratio}},
timeout=30,
)
resp.raise_for_status()
return resp.json()The test side replays recorded JSON by patching requests.post.
# tests/conftest.py (replay part)
import json
import pathlib
import pytest
CASSETTE_DIR = pathlib.Path(__file__).parent / "cassettes"
class _FakeResponse:
def __init__(self, payload, status=200):
self._payload = payload
self.status_code = status
def raise_for_status(self):
if self.status_code >= 400:
import requests
raise requests.HTTPError(f"{self.status_code}")
def json(self):
return self._payload
@pytest.fixture
def replay(monkeypatch):
def _use(cassette_name):
data = json.loads((CASSETTE_DIR / cassette_name).read_text())
def fake_post(url, **kwargs):
return _FakeResponse(data["response"], data.get("status", 200))
monkeypatch.setattr("requests.post", fake_post)
return data
return _useEach cassette keeps its recording timestamp and request metadata. That decision pays off later.
{
"recorded_at": "2026-06-20T09:12:44+09:00",
"request": {
"model": "imagen-4.0-fast-generate-001",
"endpoint": ":predict",
"parameters": {"sampleCount": 1, "aspectRatio": "9:16"}
},
"response": {
"predictions": [{"bytesBase64Encoded": "iVBORw...", "mimeType": "image/png"}],
"modelVersion": "imagen-4.0-fast-generate-001"
}
}On top of this I wrote six tests — prediction shape, image bytes, aspect ratio, batch output, the empty-predictions case for safety-filtered prompts. On my sandbox VM (Python 3.10.12):
$ python3 -m pytest tests/ -q
...... [100%]
6 passed in 0.11s
All green. Meanwhile, the recorded model, imagen-4.0-fast-generate-001, retires on August 17. Since the post-retirement behavior cannot be observed yet, I stood up a local stub that mirrors the NOT_FOUND shape returned by previously retired model families, and compared:
imagen-4.0-fast-generate-001: HTTPError 404 Client Error: Not Found (4.1ms)
gemini-3.1-flash-lite-image: OK modelVersion=gemini-3.1-flash-lite-image (2.2ms)
The suite passes in 0.11 seconds; the same code against a live-shaped endpoint dies with a 404. That gap is what "green but dead" means. For what actually happens when production keeps retrying a retired model, I measured it separately in Retired-model retries never show up in your success latency — permanent errors have a way of hiding from healthy-path metrics too.