Gemini 4 Argon API finish_reason: CONTINUATION and Empty Replies
Gemini 4 Argon: resend with continuationToken on CONTINUATION, raise the budget when MAX_TOKENS has no text. OpenAI-compatible endpoints can't continue.
On this page

Gemini 4 Argon (gemini-4-argon) cuts answers short for two ordinary reasons, and your code has to tell them apart. CONTINUATION means the model reached the per-request cap (65,536 output tokens, or 32,768 on a non-streaming REST call) with budget still left, so send the identical request again with the continuationToken it returned and repeat until the reason changes. MAX_TOKENS means the whole maxOutputTokens budget is gone, thinking included. If that reply has no text at all, every token went to thinking: raise maxOutputTokens to at least 32,768 or lower thinkingLevel.
Whether you ever see CONTINUATION depends on how you call Argon. Python google-genai 2.30.1+ and JavaScript @google/genai 2.29.1+ continue on the native generateContent endpoint by themselves and hand you one combined answer ending in STOP; older SDKs return the first, often empty, piece without raising an error. OpenAI-compatible endpoints, Google's included, cannot continue, and a gateway that drops continuationToken leaves you an empty answer with nothing to resume. For answers past 65,536 tokens, use a current SDK on the native endpoint, your own REST loop, or the Interactions API with background: true.
On October 28, 2026, Argon was called through one third-party gateway on its Gemini-native and OpenAI-compatible endpoints, and the Python SDK was run against a local mock server. No direct Google API key was used, and a real CONTINUATION never came back from that gateway, so statements about Google's direct behavior come from Google's long decode guide for generateContent and the Gemini API generateContent reference.
What each Gemini 4 Argon finish_reason means and what to do next
Continue only when the reason is CONTINUATION and a token came with it; every other value ends the loop and tells you what to change before the next request. The field is finishReason in REST JSON and finish_reason in the Python SDK and in OpenAI-style responses. Argon only outputs text, so the image reasons in the enum (IMAGE_SAFETY, NO_IMAGE and the rest) don't occur. The meanings below are from the generateContent API reference and Google's long decode guide, as of October 28, 2026.
| finishReason | What it means on Argon | What your code does next |
|---|---|---|
STOP | The model ended on its own or hit a stop sequence | Treat as finished, but check that the text actually does the task |
CONTINUATION with continuationToken | Per-request cap reached, budget left; the text can be empty because the hop was all thinking | Resend the identical request plus the latest token |
CONTINUATION without a token | Something between you and Google dropped the field | You can't resume; call the native API directly or use Interactions with background: true |
MAX_TOKENS with text | All of maxOutputTokens is used, thinking included | Direct from Google, the reply still carries a token: resend with it and a higher maxOutputTokens, or start over without it and pay for both runs |
MAX_TOKENS with empty text | The whole budget went to thinking | Raise maxOutputTokens (32,768 at minimum) or lower thinkingLevel |
| No value, or an empty string, on the last stream chunk | The connection ended before the model did | Treat as incomplete; resume from the last checkpoint token or resend |
SAFETY, PROHIBITED_CONTENT, BLOCKLIST, SPII, RECITATION, LANGUAGE | Content stop | Change the prompt or input; resending the same request won't help |
MALFORMED_FUNCTION_CALL, UNEXPECTED_TOOL_CALL, TOO_MANY_TOOL_CALLS, MISSING_THOUGHT_SIGNATURE, MALFORMED_RESPONSE | Tool-call or response format problem | Fix tool declarations, enable tools you expect to be called, or send back the thought signatures you received |
ESCALATION, PUP_LIMITED_DISABLED | Filtered by an escalation rule, or the account is limited for Prohibited Use Policy violations | Not a code fix; check the account and the content policy |
OTHER | Unspecified | Log finishMessage and retry once |

STOP only means the model stopped by itself, not that it did what you asked. In a test on October 28, Argon was asked to print every integer from 1 to 7,000 with a 33,000-token budget; it answered in 142 tokens that the list would exceed its output limit, suggested seq 1 7000, and ended with STOP. If your pipeline checks only the finish reason, that refusal passes as a success.
The same rules fit in one function that maps a REST response to a next step. This function and the openai_truncated check further down were run on October 28, 2026 against seven saved gateway responses and four hand-written cases; all eleven gave the expected result, including the empty-string reason on a cut stream.
def next_action(resp):
cands = resp.get("candidates") or []
if not cands:
return "prompt blocked or no candidate: log the body, fix the prompt"
cand = cands[0]
reason, token = cand.get("finishReason") or None, cand.get("continuationToken")
parts = (cand.get("content") or {}).get("parts") or []
text = "".join(p.get("text", "") for p in parts if not p.get("thought"))
if reason is None:
return "incomplete: no finishReason; resume from the last checkpoint token or resend"
if reason == "STOP":
return "done (still check that the text does the task)"
if reason == "CONTINUATION":
return "continue: resend with continuationToken" if token else \
"token dropped: call the native API directly or use Interactions"
if reason == "MAX_TOKENS":
why = "empty, all thinking; " if not text else ""
return why + ("raise maxOutputTokens, resend with token" if token else "raise maxOutputTokens, start over")
if reason in {"SAFETY", "PROHIBITED_CONTENT", "BLOCKLIST", "SPII", "RECITATION", "LANGUAGE"}:
return "content stop: change the prompt or input"
if reason in {"MALFORMED_FUNCTION_CALL", "UNEXPECTED_TOOL_CALL", "TOO_MANY_TOOL_CALLS",
"MISSING_THOUGHT_SIGNATURE", "MALFORMED_RESPONSE"}:
return "tool or format stop: fix tool declarations or return thought signatures"
return f"other ({reason}): log finishMessage {cand.get('finishMessage')!r}, retry once"How to access the Gemini 4 Argon API: paid key, model ID, SDK version
Argon is a paid-tier model on the Gemini API: Google's Gemini API pricing page lists it as not available on the free tier, and every token is billed, thinking tokens included. Most early cut-offs come from skipping one of the setup steps below, so check them in order before you debug finish reasons.
- Put the Google Cloud project behind your key on a paid tier: link billing and prepay at least $5 for Tier 1. A free-tier key can't call Argon, and a key whose prepaid balance reaches $0 stops with HTTP 402.
- Call the model ID
gemini-4-argon, generally available since October 13, 2026 according to the Gemini API changelog. - Upgrade the SDK:
pip install -U "google-genai>=2.30.1"ornpm install "@google/genai@>=2.29.1"(Go 1.74.0, Java 1.77.0, .NET 1.26.0 and Kotlin 1.6.0 are the other minimums). - Set
max_output_tokensto at least 32,768 for ordinary apps and above 65,536 for long answers. It's one total for thinking and output across every hop, and lowering it doesn't make thinking shorter, it only cuts the answer sooner. - Remove
temperature,top_pandtop_k(Argon ignores them), and setthinking_leveltolow,medium(the default) orhigh, neverminimaland never together withthinking_budget. What each level changes is in Gemini 3.1 Pro Thinking Levels: Low vs High, Defaults, Gemini 4. - Set the HTTP read timeout to 20 minutes, for example
genai.Client(http_options=types.HttpOptions(timeout=1_200_000))in Python. Google closes non-streaming calls after 10 minutes and streams after 20, and a 3- or 5-minute client timeout ends hard requests early.
A Google AI Pro or Ultra subscription doesn't switch on API calls for a project by itself; what the plans include for Argon in the app, AI Studio and API credits is covered in Gemini 4 Argon Usage Limits: Pro vs Ultra, Resets, API Credits. As of October 28, 2026, Argon costs $2 input and $10 output per million tokens until November 30, then $4 and $20 from December 1; the full price list is in Gemini API Pricing 2026: Token Rates, Free Tier, and Key Cost. The API serves the United States, Japan, South Korea and Spain among others, but not mainland China, Hong Kong, Russia or Iran, per Google's available regions list.
Which Argon endpoints can continue an answer past 65,536 tokens
Only two setups produce a long Argon answer today: the native generateContent endpoint with someone running the continuation (a current SDK or your own loop), and the Interactions API with background execution. Google's long decode guide states that on the OpenAI compatibility endpoint "Long decode is not available. Output stops at the per-request cap." A gateway sits in front of one of these, so it can continue only if it passes continuationToken through in both directions.
| How you call Argon | Can it continue? | How the end is signaled |
|---|---|---|
google-genai ≥ 2.30.1 / @google/genai ≥ 2.29.1, native endpoint | Yes, automatically (automatic_continuation defaults to on) | One combined response; finish_reason of the last hop |
| Older SDK versions | No; output stops at 64K | CONTINUATION returned to you, no error |
REST generateContent / streamGenerateContent | Yes, with your own loop (32K per non-streaming hop, 64K per stream) | finishReason on every hop |
Interactions API, background: true, store: true | Yes, Google runs and joins the hops, for up to 24 hours | status: completed, requires_action, incomplete, failed, cancelled |
Interactions API, background: false | No; 65,536 cap, larger values return 400 | status |
OpenAI-compatible endpoint (https://generativelanguage.googleapis.com/v1beta/openai/) | No | finish_reason; Google doesn't document how it maps |
| Third-party gateway | Only if it forwards continuationToken | Whatever the gateway returns; see the tests below |
On Interactions there is no finish_reason to read. Per Google's Interactions long decode guide, you create the interaction with background: true and max_output_tokens above 65,536, poll until the status leaves in_progress or queued, and handle incomplete the way you would handle MAX_TOKENS: the budget ran out, the text can be empty if the model was still thinking, and the fix is to resend with a higher max_output_tokens.
If you reach Argon through the OpenAI SDK, reasoning_effort maps to Argon's thinking levels on Google's endpoint, but long answers stay capped at one request. OpenAI Base URL Override: Which Setting Wins and What to Paste covers pointing the OpenAI client at another endpoint.
Why google-genai never shows you CONTINUATION
With google-genai 2.30.1 or later, CONTINUATION is handled inside the SDK: it sends the follow-up request, merges the parts, sums the usage and returns only the final reason. That's why a working setup almost never shows the value, and why seeing it in your logs usually means an old SDK or a gateway that lost the token. To confirm this without spending money, google-genai 2.33.0 (the latest on PyPI on October 28, 2026) and 2.29.0 were pointed at a local server that returned responses in the shape Google documents.
| Mock scenario | google-genai 2.33.0 | google-genai 2.29.0 |
|---|---|---|
Hop 1 CONTINUATION + token, empty text, 32,768 thinking tokens; hop 2 STOP | 2 requests; the second carried the token with the same maxOutputTokens (262,144) and the same contents. You get STOP and the final text | 1 request; you get CONTINUATION, empty text and the token, no exception |
Same, with automatic_continuation=False | 1 request; you get CONTINUATION and the token | The option doesn't exist |
CONTINUATION with no token, as from a gateway that strips it | 1 request; empty text and CONTINUATION, no exception | Same |
MAX_TOKENS + token | 1 request; partial text and the token; the SDK does not continue | Same |
The third row is the failure to guard against: no exception, HTTP 200, and an empty response.text. The SDK source (google/genai/_extra_utils.py in 2.33.0) only resumes when the finish reason is CONTINUATION or missing, and on streams it gives up after three reconnects that bring no new checkpoint token. So after any Argon call, check response.text and finish_reason together, and treat empty text with CONTINUATION as a broken connection to Google rather than a short answer.
Writing the continuation loop yourself over REST
If you call REST directly, or need the SDK's loop off (automatic_continuation=False in Python, automaticContinuation: false in JavaScript), the loop is short. The version below follows the rules in Google's long decode guide. On October 28, 2026 it was run against a local mock that returned two CONTINUATION hops and then STOP: it sent three requests with the same contents and the same maxOutputTokens, joined the text and kept the first hop's prompt count. It has not been run against Google's servers.
import os
import requests
URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-4-argon:generateContent"
HEADERS = {"x-goog-api-key": os.environ["GEMINI_API_KEY"], "Content-Type": "application/json"}
def run_long(prompt, max_output_tokens=262_144):
body = {
"contents": [{"role": "user", "parts": [{"text": prompt}]}],
"generationConfig": {"maxOutputTokens": max_output_tokens, "thinkingConfig": {"thinkingLevel": "high"}},
}
parts, usage_sum, first_prompt = [], {}, None
while True:
r = requests.post(URL, headers=HEADERS, json=body, timeout=1200) # 20-minute read timeout
r.raise_for_status()
data = r.json()
cand = data["candidates"][0]
usage = data.get("usageMetadata", {})
if first_prompt is None:
first_prompt = usage.get("promptTokenCount") # your real prompt size
for k, v in usage.items():
if isinstance(v, int):
usage_sum[k] = usage_sum.get(k, 0) + v # each REST hop reports only itself
parts += (cand.get("content") or {}).get("parts", [])
token = cand.get("continuationToken")
if cand.get("finishReason") != "CONTINUATION" or not token:
break
body["continuationToken"] = token # same contents, same config, latest token only
text = "".join(p.get("text", "") for p in parts if not p.get("thought"))
return text, cand.get("finishReason"), first_prompt, usage_sumThree rules from Google's guide matter more than the code. Send the original contents, system_instruction, tools and generation_config on every hop with the same maxOutputTokens, because the API reference requires the contents to "identically replicate" the first request. Never append the partial output to contents; the token already carries the model's state, thinking included, and each new token replaces the previous one. And stop on any reason other than CONTINUATION, then run the final reason through the table above.
Google's guide contradicts itself on one point: it shows the first non-streaming REST hop with maxOutputTokens 262,144, yet also says a non-streaming REST call above 32,768 without a token returns 400. On the gateway tested, a non-streaming request with 40,000 returned 200. If Google's API gives you that 400 on the first hop, use the SDK or streamGenerateContent instead. A streaming loop needs one more rule: after the first 32,768 tokens the stream sends a checkpoint token every 5,000 tokens, so hold new chunks as pending until a chunk with a token arrives, discard the pending ones if the stream drops, and resume from the last token.
To resume after MAX_TOKENS, send the same request with its token and a maxOutputTokens above the tokens already used; a value at or below that returns 400 "Continuation token has already used output tokens…".
Gateway and OpenAI-compatible endpoints: results from October 28, 2026
On the gateway tested, Argon never returned a continuationToken, so no answer through it can be continued. A budget that ran out started over from scratch on the next request, and a reply cut at the cap on its OpenAI-compatible endpoint came back labeled stop. The gateway was LaoZhang API (api.laozhang.ai), which serves Argon on a Gemini-native generateContent path and an OpenAI-compatible /v1/chat/completions path. Requests went from a local HTTP proxy to the gateway, and the whole set cost at most $0.59 at Google's list price, counting the two dropped long runs as fully billed.
Disclosure: laozhang.ai is our own service. Its prices are platform pricing, not an independent comparison.
| Request | What came back | What it means for your code |
|---|---|---|
Native, maxOutputTokens 64, thinkingLevel low | MAX_TOKENS, 291 characters, 64 output tokens, no continuationToken | Nothing to resume; resending the request produced a new answer from the start |
Native, maxOutputTokens 200, thinkingLevel high, hard math prompt | MAX_TOKENS, 0 characters of text (197 thinking + 3 output tokens), HTTP 200 | An empty answer with no error; check the text, not the status code |
Native non-streaming, maxOutputTokens 40,000, "Reply with exactly: OK" | STOP, "OK" | No 400 here, although Google documents one for direct REST above 32,768 |
Native, thinkingLevel "minimal" | HTTP 500 with a generic "upstream service error" message in Chinese and code "402" | Argon rejects minimal; the gateway hid Google's actual error text |
OpenAI-compatible, max_tokens 64, same long prompt | completion_tokens 64, finish_reason "stop" | A cut reply labeled stop; compare completion_tokens with max_tokens |
Native non-streaming, maxOutputTokens 33,000, 2,500-row CSV | Connection closed at 300.9 seconds, no body | No reason, no usage, nothing to resume |
streamGenerateContent, same CSV | 74,760 characters (up to row 1,065) over 993 events, then the stream ended at 302.5 seconds | See the next section |
Two rules follow for any gateway. First, on an OpenAI-style endpoint, don't trust finish_reason alone: if completion_tokens reached max_tokens, treat the reply as truncated and raise max_tokens, because that endpoint cannot continue. Second, check a gateway before relying on it for long answers. Google's guide says a MAX_TOKENS reply from its API still carries a token, so a cheap test is a single request with maxOutputTokens 64: if the reply ends in MAX_TOKENS without continuationToken, that gateway won't carry a long answer.
def openai_truncated(resp, max_tokens):
used = (resp.get("usage") or {}).get("completion_tokens", 0)
return resp["choices"][0].get("finish_reason") == "length" or used >= max_tokensThe ~300-second cut on both long runs is shorter than Google's documented 10- and 20-minute server limits. It happened somewhere between the client and the gateway, a path that included the local HTTP proxy, and which of the two closed the connection was not determined. If a request sent through your own proxy or gateway keeps dying near five minutes, check the timeouts on every layer you control before blaming the model.
Stream ended with no finishReason: incomplete, not done
A stream that ends without a finish reason did not finish, even if it returned plenty of text. In the October 28 test the last chunk carried "finishReason": "" (an empty string), the intermediate chunks reported candidatesTokenCount 0, and no final usage or checkpoint token ever arrived; the CSV stopped at row 1,065 of 2,500. Code that treats "stream closed without an exception" as success would have shipped less than half the file.
Treat a missing or empty reason as incomplete. If you saved a checkpoint token, send the original request again with it and drop the chunks received after that checkpoint. If the stream dropped before the first checkpoint, which only appears after 32,768 tokens, resend the original request. Google's guide says a stream dropped before 32K tokens costs nothing, and after that you pay up to the last checkpoint. The SDK does this resume for you, up to three reconnects without progress.
Why prompt_token_count looks huge after long decode
After a multi-hop answer, prompt_token_count counts every hop's input, and each hop re-sends your prompt plus everything generated so far. Google's guide gives the arithmetic for a 1,000-token prompt that produces 200,000 tokens of thinking and output in four SDK hops: 1,000 + 66,536 + 132,072 + 197,608 = 397,216 input tokens reported, and about 597,000 total, while the real context is about 201,000 tokens. The same job over non-streaming REST takes seven 32K hops and reports 695,128 input tokens.

The SDK mock run showed the same pattern at small scale: a 120-token prompt reported prompt_token_count 33,008 after two hops (120 + 32,888), while initial_prompt_token_count stayed at 120. Read initial_prompt_token_count (Python) or initialPromptTokenCount (JavaScript) for prompt size and context trimming. The SDK computes it on the client, so it isn't in REST responses or in model_dump(); save it separately if you log responses. Over REST, add each usage field across hops for cost and keep the first hop's promptTokenCount as the prompt size, as the loop above does. A gateway that calls REST for you has to do the same, or its usage numbers won't match what the SDK reports for the same request.
For cost, you pay for every token in every hop, including hops that are pure thinking. Implicit caching discounts the repeated input in each hop by 95%, $0.10 instead of $2.00 per million tokens during the introductory pricing, but the cache expires after 15–20 minutes idle, so a long pause between hops brings back the full input rate. A failed non-streaming hop costs nothing. If the bills are higher than expected because Argon is the wrong model for the job, Gemini 4 Argon vs 3.8 Flash: Cost per Task and When to Switch compares the same tasks on both.
Sources6
External pages this guide links to, in the order they appear. Last updated Oct 28, 2026.
Sources6
External pages this guide links to, in the order they appear. Last updated Oct 28, 2026.
- 1.long decode guide for generateContentai.google.dev/gemini-api/docs/generate-content/long-decode
- 2.Gemini API generateContent referenceai.google.dev/api/generate-content
- 3.Gemini API pricing pageai.google.dev/gemini-api/docs/pricing
- 4.Gemini API changelogai.google.dev/gemini-api/docs/changelog
- 5.available regions listai.google.dev/gemini-api/docs/available-regions
- 6.Interactions long decode guideai.google.dev/gemini-api/docs/long-decode





