Switching from Gemini 2.5 Pro or 3.1 to Gemini 3.2 and suddenly watching your code fall apart is a frustrating experience — and a surprisingly common one. I've been through it myself while maintaining personal apps, and it cost me a few hours before I tracked down the root causes.
Gemini 3.2 is a significant upgrade, but that comes with real API changes that don't always play nicely with code written for older versions. Below are five error patterns I've either hit directly or seen frequently in developer communities, along with concrete fixes for each.
Error 1: Wrong Model ID (400 INVALID_ARGUMENT)
The most common stumbling block by far. At launch, plenty of developers tried variations like gemini-3-2-pro or gemini-3.2-pro-latest and got nowhere.
import google.generativeai as genai
# ❌ These won't work
model = genai.GenerativeModel("gemini-3-2-pro") # hyphen-based form is wrong for 3.2
model = genai.GenerativeModel("gemini/3.2-pro") # slash notation is invalid
model = genai.GenerativeModel("gemini-3.2-pro-latest") # "latest" has inconsistent availability
# ✅ Correct model IDs
model = genai.GenerativeModel("gemini-3.2-pro") # dot notation is official
model = genai.GenerativeModel("gemini-3.2-flash") # lightweight optionDocumentation sometimes lags behind releases, so it's worth running a live model list query when in doubt:
import google.generativeai as genai
genai.configure(api_key="YOUR_GEMINI_API_KEY")
for m in genai.list_models():
if "gemini-3.2" in m.name:
print(m.name, "—", m.supported_generation_methods)Expected output (as of May 2026):
models/gemini-3.2-pro — ['generateContent', 'streamGenerateContent']
models/gemini-3.2-flash — ['generateContent', 'streamGenerateContent']
Error 2: Context Window Overflow (400 RESOURCE_EXHAUSTED)
Gemini 3.2 Pro's context window extends beyond 2M tokens, which sounds like a solution to all context problems — until it creates a new one. The expanded limit makes it easy to forget about token management, and hitting the ceiling produces an opaque RESOURCE_EXHAUSTED error.
The problematic pattern
# ❌ Unbounded conversation history — will eventually overflow
conversation_history = []
def chat(user_input):
conversation_history.append({"role": "user", "parts": [user_input]})
response = model.generate_content(conversation_history)
conversation_history.append({"role": "model", "parts": [response.text]})
return response.text# ✅ Fixed version — monitor tokens and compress with rolling summary
MAX_TOKENS = 1_500_000 # Set limit at 75% of the 2M ceiling
def chat_with_limit(user_input, history):
history.append({"role": "user", "parts": [user_input]})
token_count = model.count_tokens(history).total_tokens
if token_count > MAX_TOKENS:
# Summarize older context, keep recent exchanges intact
summary = summarize_history(history[:-10])
history = [{"role": "user", "parts": [f"[Conversation summary]: {summary}"]}] + history[-10:]
response = model.generate_content(history)
history.append({"role": "model", "parts": [response.text]})
return response.text, historyFor a deeper look at context management strategies during migration, the Gemini 3.2 Pro Migration Playbook covers this in detail.
Error 3: Streaming Response Cuts Off Mid-Output
Gemini 3.2 enables Thinking Mode by default in certain configurations, which introduces a timing disconnect between the model's internal reasoning process and its actual text output. This manifests as empty chunks during streaming that crash naive implementations.
What it looks like
GenerateContentResponse(
done=True,
candidates=[Candidate(
content=Content(parts=[Part(text='')]),
finish_reason=FinishReason.STOP,
)]
)
# ❌ Crashes on empty chunks
for chunk in model.generate_content(prompt, stream=True):
print(chunk.text) # ValueError on empty candidate
# ✅ Safely skip empty chunks
for chunk in model.generate_content(prompt, stream=True):
if chunk.candidates and chunk.candidates[0].content.parts:
for part in chunk.candidates[0].content.parts:
if hasattr(part, 'text') and part.text:
print(part.text, end='', flush=True)To control Thinking Mode explicitly:
from google.generativeai.types import GenerationConfig
# Disable thinking for lower latency
config = GenerationConfig(
thinking_config={"thinking_budget": 0}
)
response = model.generate_content(prompt, generation_config=config)
# Enable thinking with a token budget for higher accuracy
config_thinking = GenerationConfig(
thinking_config={"thinking_budget": 10000}
)Error 4: Rate Limit Errors (429 RESOURCE_EXHAUSTED)
Gemini 3.2 usage has spiked since launch, and rate limits hit harder for free-tier and lower-paid plan users. From running production apps, I've noticed limits tend to be tightest during morning peaks (9–10 AM JST) and the late-night window around 2–3 AM.
import time
import random
from google.api_core import exceptions
def generate_with_retry(model, prompt, max_retries=3):
"""Exponential backoff with jitter for rate limit handling."""
for attempt in range(max_retries):
try:
return model.generate_content(prompt)
except exceptions.ResourceExhausted as e:
if attempt == max_retries - 1:
raise
# 1s → 2s → 4s + random jitter
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limited — retrying in {wait:.1f}s (attempt {attempt + 1}/{max_retries})")
time.sleep(wait)
except exceptions.ServiceUnavailable:
if attempt == max_retries - 1:
raise
time.sleep(5 + random.uniform(0, 2))More detailed strategies are in the Gemini API Rate Limit Error guide.
Error 5: Function Calling Schema Validation (400 INVALID_ARGUMENT)
Gemini 3.2 tightened schema validation for Function Calling. Code that worked fine in 3.1 sometimes fails outright with a schema error.
Error message to watch for
google.api_core.exceptions.InvalidArgument: 400 Function declarations must not have 'optional' keys.
# ❌ Worked in Gemini 3.1, fails in 3.2
tools_old = [
{
"function_declarations": [{
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "optional": True} # ← deprecated key
},
"required": ["city"]
}
}]
}
]
# ✅ Gemini 3.2-compatible schema
tools_new = [
{
"function_declarations": [{
"name": "get_weather",
"description": "Get current weather for a city. Defaults to Celsius if unit is omitted.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name (e.g. Tokyo, London)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit. Defaults to celsius."
# No "optional" key — omit from "required" list instead
}
},
"required": ["city"] # "unit" left out = optional
}
}]
}
]Debugging tip
When a schema fails, start with the smallest possible definition and add properties one at a time:
def validate_schema(model, tool_schema):
try:
response = model.generate_content(
"Test: what's the weather in Tokyo?",
tools=tool_schema
)
print("✅ Schema validated OK")
return True
except Exception as e:
print(f"❌ Schema error: {e}")
return FalseQuick Reference: Migration Checklist
When Gemini 3.2 throws an error after migration, run through these five checks before digging deeper:
- Is the model ID using dot notation:
gemini-3.2-proorgemini-3.2-flash? - Is the context approaching 2M tokens? (Use
count_tokensto verify) - Does your streaming handler skip empty chunks?
- Is there exponential backoff retry logic for 429 errors?
- Does your Function Calling schema use
required/non-required fields instead of theoptionalkey?
Each Gemini release brings changes that aren't always reflected immediately in the docs. My approach has been to test early, keep a minimal reproduction of each integration, and treat the SDK's error messages as the ground truth rather than cached assumptions. If you've hit a different error pattern not covered here, the Gemini 3.2 API implementation guide has a broader reference to work from.