Gemini 3.7 Flash 400 Error? Fix Thinking Overflow
Woke up to a wall of
400 INVALID_ARGUMENT errors after swapping your model string to gemini-3.7-flash? You're not alone. Since the August 13, 2026 GA release, a lot of production pipelines that quietly worked for months started throwing errors or silently truncating responses mid-JSON. The root cause almost never shows up in the error message itself — it's buried in how the new thinking-token budget interacts with your old generation_config. This is a debugging log from someone who spent an afternoon staring at finishReason: MAX_TOKENS before figuring out exactly what changed under the hood.
Why Your Old Config Suddenly Breaks
gemini-3.7-flash: the scratch paper and the exam sheet share the same page limit. Both draw from the same 65,536-token maxOutputTokens pool. If the model spends too many pages thinking, there's nothing left to write the actual answer, and the API cuts it off with MAX_TOKENS before a single character of your JSON reaches you.
That single design detail explains almost every ticket in the "Gemini 3.7 Flash suddenly broken" threads.
Root Cause #1: The THINKING_LEVEL_MINIMAL Trap
If your codebase (or a proxy layer like a self-hosted LiteLLM gateway) was written for older Flash-tier models, there's a good chance it still sends thinking_level: "minimal" or "none" to shave off latency. gemini-3.7-flash doesn't recognize either value. It only accepts three tiers:
| thinking_level | Typical Use Case | Behavior on gemini-3.7-flash |
|---|---|---|
| low | High-throughput chat, autocomplete, simple classification | Minimal reasoning overhead, fastest response, closest to legacy "no thinking" behavior |
| medium (default) | General agentic tasks, tool calling, coding assistants | Balanced reasoning depth, recommended starting point |
| high | Multi-step math, complex refactors, long-horizon planning | Deepest reasoning chain, consumes the largest share of the output pool |
Send anything outside this table — minimal, none, disable — and the API rejects the call outright with:
400 INVALID_ARGUMENT: Thinking level is unsupported: THINKING_LEVEL_MINIMAL
There's no silent fallback. It's a hard stop. The fix is mechanical: swap
minimal for low, redeploy, done. But that alone doesn't solve the truncation problem — that's a separate, sneakier bug.
Root Cause #2: Output Token Starvation
Let's say you migrated your thinking_level correctly to "high" for a complex code-refactor agent, but you kept an old, conservative maxOutputTokens: 2048 from a previous cost-control pass. Here's what happens step by step:
Request sent (thinking_level=high, maxOutputTokens=2048)
│
▼
Model starts internal reasoning
│
▼
Reasoning tokens exceed 2048? ──── No ──▶ Reasoning completes ──▶ Full response returned
│
Yes
│
▼
Token budget exhausted mid-thought
│
▼
finishReason: MAX_TOKENS
│
▼
Empty or truncated final response body
With thinking_level: "high", the model can easily burn 1,500–1,900 tokens just deliberating before it writes a single word of the actual answer. A 2,048 cap leaves almost no room, and you get an empty text field with a MAX_TOKENS finish reason — which looks like a network bug, not a config bug.
thinking_level: "low" rarely needs more than 4,096–8,192 total. A multi-step agentic coding task at "high" should get 16,384 or higher, or you should simply omit maxOutputTokens and let it default to the full 65,536 ceiling.
Step-by-Step Fix: Production Code
Below are drop-in configs for both the Python and Node.js SDKs. Pay close attention to what's removed — temperature, top_p, and top_k are deprecated for this model family and should not be manually overridden.
Python (google-genai)
from google import genai
from google.genai import types
# Initialize the client using your API key from environment variables.
client = genai.Client(api_key="YOUR_API_KEY")
# Define the generation config explicitly.
# Note: we do NOT set temperature, top_p, or top_k here.
# Gemini 3.7 Flash deprecates manual sampling overrides —
# leaving them at model defaults avoids decode-loop regressions.
config = types.GenerateContentConfig(
# thinking_level must be one of: "low", "medium", "high".
# "minimal" or "none" will raise a 400 INVALID_ARGUMENT error.
thinking_config=types.ThinkingConfig(
thinking_level="medium"
),
# Give the model enough room for BOTH thinking AND the final answer.
# 16384 is a safe floor for agentic / coding tasks.
# Omit this field entirely to default to the full 65536 ceiling.
max_output_tokens=16384,
# Structured output is fine, just don't pair it with low token budgets.
response_mime_type="application/json",
)
response = client.models.generate_content(
model="gemini-3.7-flash",
contents="Refactor this function to remove the nested loop and explain why.",
config=config,
)
# Always check finish_reason before trusting response.text is complete.
if response.candidates[0].finish_reason == "MAX_TOKENS":
print("Truncated! Increase max_output_tokens or lower thinking_level.")
else:
print(response.text)
Node.js / TypeScript (@google/genai)
import { GoogleGenAI } from "@google/genai";
// API key is pulled from process.env.GEMINI_API_KEY by default.
const ai = new GoogleGenAI({});
async function runAgentTask() {
// generationConfig mirrors the Python SDK structure.
// thinkingConfig.thinkingLevel accepts only "low" | "medium" | "high".
const response = await ai.models.generateContent({
model: "gemini-3.7-flash",
contents: "Summarize this 40-page PDF into 5 bullet points.",
config: {
thinkingConfig: {
// "low" is ideal here since summarization doesn't need deep reasoning.
thinkingLevel: "low",
},
// Even simple tasks need headroom; don't starve the output pool.
maxOutputTokens: 8192,
// No temperature/topP/topK overrides — leave sampling at model defaults.
},
});
// Defensive check: log finishReason so truncation never fails silently.
const finishReason = response.candidates?.[0]?.finishReason;
if (finishReason === "MAX_TOKENS") {
console.warn("Output was cut short — raise maxOutputTokens or lower thinkingLevel.");
}
console.log(response.text);
}
runAgentTask();
The pattern is consistent across both SDKs: pick a thinking_level that matches task complexity, give it a realistic token ceiling, and stop touching sampling parameters that no longer apply.
Advanced Troubleshooting: JSON Schema Loops
If you're forcing responseMimeType: "application/json" with a strict schema and you manually pinned temperature to something low like 0.2, watch for a specific failure mode: the model gets stuck re-generating the same digit or field repeatedly until it hits the token ceiling. This looks like an infinite loop but it's actually a decode conflict between the forced low-temperature sampling and the schema constraint solver.
- ☑ Remove any explicit
temperature,top_p,top_kvalues fromgeneration_config - ☑ Replace
thinking_level: "minimal"/"none"with"low" - ☑ Raise
maxOutputTokensproportionally tothinking_level(low → 4k-8k, medium → 8k-16k, high → 16k+) - ☑ Add a
finishReasoncheck in your response handler — don't assumeresponse.textis complete - ☑ Re-test structured JSON schema calls specifically; loop bugs rarely show up in plain-text prompts
If you're also comparing reasoning depth against other current-generation models like Claude Sonnet 5 or GPT-5.6 Sol for the same agentic workload, the same principle applies broadly: reasoning-heavy models need proportionally larger output budgets, not just longer prompts.
Wrapping Up
The 400 error is the easy part to fix — swap minimal for low and move on. The truncation bug is the one that actually costs you debugging hours, because it fails silently and looks like a network issue. Once you understand that thinking tokens and output tokens share one pool, the fix is just arithmetic: give the model enough room to think and answer.
If this saved you from an afternoon of console-log archaeology, drop a comment with what error message you were hitting — it helps other developers hitting the same wall find the fix faster.
댓글
댓글 쓰기