You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Requests ending with a model turn are not supported #45359
Provider: Google Gemini API (AI Studio)
Model: Gemini 3.7 Flash
issue: Requests ending with a model turn are not supported
happens when the model is usually overloaded, and opencode's internal retry mechanism doesn't handle this edge case.
When does it happen? after the model finishes thinking.
Since when was this happening? Ver. 1.18.21 or above
Plugins
No
OpenCode version
1.18.23
Steps to reproduce
Choose Google Gemini API, Use any gemini model (we'll choose gemini 3.7 flash)
Try to normally user the model
You'll observe after a few turns, the exact issue: Bad Request: {"detail":{"error":{"code":400,"message":"Requests ending with a model turn are not supported.","status":"INVALID_ARGUMENT"}}}
You'lll again have to input prompt to be able to continue
Assistant prefill works on Anthropic but Gemini has no equivalent and rejects it. Reproduced against gemini-3.8-flash with identical history, only the last turn's role differing:
contents ending on a model turn -> 400 INVALID_ARGUMENT: Requests ending with a model turn are not supported.
contents ending on a user turn -> 200
That also explains why it mostly shows up on subagents: steps has no default, so it only triggers on agents that configure it.
Sending that prompt as a user turn fixes it. I checked the instruction still lands — the model replies with the "maximum steps reached" summary it asks for — and tools are already disabled for that step via toolChoice: "none", so the constraint isn't relying on the message role. It's also consistent with how the runner already injects synthetic input, compaction checkpoints and chronological system updates, which all ride in user turns.
I have this working on both dev and v2 with the suites passing. Happy to open a PR if useful — which branch would you prefer? #40455 looks like the same prefill causing a 400 on Claude 4.6+, so a fix here would likely cover that too.
Thanks for the thorough investigation @ianstrom-davinci! You're spot on regarding the root cause with MAX_STEPS_PROMPT and trailing assistant turns triggering 400s across Gemini (and Claude 4.6+ in #40455).
We are addressing this by moving MAX_STEPS_PROMPT into the system prompt array directly (rather than appending a synthetic user turn or assistant prefill) across both prompt.ts and session/runner/llm.ts. This maintains clean conversation history, keeps the system constraint in system context without altering turn structure, and prevents consecutive-user turn issues when step limits trigger after user input.
Additionally, we've added Gemini-specific sequence normalization in ProviderTransform to handle edge cases like consecutive role turns, empty turns, and tool-response to user-prompt transitions.
timestamp=2026-09-15T15:50:02.480Z level=ERROR run=[REDACTED] message="stream error" providerID=google modelID=gemini-3.8-flash session.id=[REDACTED] small=false agent=build mode=primary error.error="AI_APICallError: Requests ending with a model turn are not supported."
timestamp=2026-09-15T15:50:02.483Z level=ERROR run=[REDACTED] message=process session.id=[REDACTED] messageID=[REDACTED] error="Requests ending with a model turn are not supported." stack="AI_APICallError: Requests ending with a model turn are not supported.\n at <anonymous> (/$bunfs/root/chunk-yhh8qky9.js:6:14258)\n at async <anonymous> (/$bunfs/root/chunk-yhh8qky9.js:6:12506)\n at processTicksAndRejections (native:7:39)"
timestamp=2026-09-15T15:50:20.429Z level=ERROR run=[REDACTED] message="stream error" providerID=google modelID=gemini-3.8-flash session.id=[REDACTED] small=false agent=build mode=primary error.error="AI_APICallError: Requests ending with a model turn are not supported."
timestamp=2026-09-15T15:50:20.430Z level=ERROR run=[REDACTED] message=process session.id=[REDACTED] messageID=[REDACTED] error="Requests ending with a model turn are not supported." stack="AI_APICallError: Requests ending with a model turn are not supported.\n at <anonymous> (/$bunfs/root/chunk-yhh8qky9.js:6:14258)\n at async <anonymous> (/$bunfs/root/chunk-yhh8qky9.js:6:12506)\n at processTicksAndRejections (native:7:39)"
timestamp=2026-09-15T15:52:34.656Z level=ERROR run=[REDACTED] message="stream error" providerID=google modelID=gemini-3.8-flash session.id=[REDACTED] small=false agent=build mode=primary error.error="AI_APICallError: Requests ending with a model turn are not supported."
timestamp=2026-09-15T15:52:34.660Z level=ERROR run=[REDACTED] message=process session.id=[REDACTED] messageID=[REDACTED] error="Requests ending with a model turn are not supported." stack="AI_APICallError: Requests ending with a model turn are not supported.\n at <anonymous> (/$bunfs/root/chunk-4h8zz3st.js:6:14258)\n at async <anonymous> (/$bunfs/root/chunk-4h8zz3st.js:6:12506)\n at processTicksAndRejections (native:7:39)"
timestamp=2026-09-15T15:55:15.873Z level=ERROR run=[REDACTED] message="stream error" providerID=google modelID=gemini-3.8-flash session.id=[REDACTED] small=false agent=build mode=primary error.error="AI_APICallError: Requests ending with a model turn are not supported."
timestamp=2026-09-15T15:55:15.877Z level=ERROR run=[REDACTED] message=process session.id=[REDACTED] messageID=[REDACTED] error="Requests ending with a model turn are not supported." stack="AI_APICallError: Requests ending with a model turn are not supported.\n at <anonymous> (/$bunfs/root/chunk-4h8zz3st.js:6:14258)\n at async <anonymous> (/$bunfs/root/chunk-4h8zz3st.js:6:12506)\n at processTicksAndRejections (native:7:39)"
Reacted by Luthfi Nugroho, Diamond, sverredanger, karawedi and LoftKing
There is a second trigger for this beyond MAX_STEPS_PROMPT: silent safety/policy suppressions where Gemini emits reasoning tokens but zero output tokens.
We hit this repeatedly on gemini-3.8-flash in v1.18.32 during code audits and security/auth reviews:
Gemini runs its thinking/reasoning pass (emitting 200–1500 reasoning tokens).
During internal safety/risk classification, the provider halts generation before generating candidate text or tool calls (output: 0, finish: "unknown").
The runner treats this empty output as an incomplete turn and loops to step=1.
The history serialized for step=1 ends on the empty assistant/reasoning turn. Gemini immediately rejects it with HTTP 400 (Requests ending with a model turn are not supported.).
Regarding #51104: blindly appending a synthetic user Continue. message when the trailing turn is assistant won't fix this scenario. If the provider suppressed the output due to content policy, prompting Continue. will just trigger another empty response or loop.
The runner needs to detect when a Gemini turn completes with reasoning tokens but zero candidate output/tools, and surface that the response was withheld (e.g. content/safety filter triggered) instead of cascading into a continuation loop or a 400 error.
Also blocked responses are recognized and halted on, they will never auto continue unless it is a completely custom provider that doesnt send an existing recognized stop reason.
Activity
ianstrom-davinci commentedon Sep 4, 2026
The cause looks like the step-limit prompt being appended as an assistant message, which makes the request end on a model turn.
Both request-building paths do this.
packages/opencode/src/session/prompt.ts:1281(AI SDK path):and
packages/core/src/session/runner/llm.ts:218(native path,:225onv2):Assistant prefill works on Anthropic but Gemini has no equivalent and rejects it. Reproduced against
gemini-3.8-flashwith identical history, only the last turn's role differing:That also explains why it mostly shows up on subagents:
stepshas no default, so it only triggers on agents that configure it.Sending that prompt as a user turn fixes it. I checked the instruction still lands — the model replies with the "maximum steps reached" summary it asks for — and tools are already disabled for that step via
toolChoice: "none", so the constraint isn't relying on the message role. It's also consistent with how the runner already injects synthetic input, compaction checkpoints and chronological system updates, which all ride in user turns.I have this working on both
devandv2with the suites passing. Happy to open a PR if useful — which branch would you prefer? #40455 looks like the same prefill causing a 400 on Claude 4.6+, so a fix here would likely cover that too.Shra1V32 commentedon Sep 4, 2026
Thanks for the thorough investigation @ianstrom-davinci! You're spot on regarding the root cause with
MAX_STEPS_PROMPTand trailing assistant turns triggering 400s across Gemini (and Claude 4.6+ in #40455).We are addressing this by moving
MAX_STEPS_PROMPTinto thesystemprompt array directly (rather than appending a syntheticuserturn or assistant prefill) across bothprompt.tsandsession/runner/llm.ts. This maintains clean conversation history, keeps the system constraint in system context without altering turn structure, and prevents consecutive-user turn issues when step limits trigger after user input.Additionally, we've added Gemini-specific sequence normalization in
ProviderTransformto handle edge cases like consecutive role turns, empty turns, and tool-response to user-prompt transitions.Opening a PR with this fix shortly!
vuhnger commentedon Sep 15, 2026
Still hitting this on version 1.18.31.
Log excerpts:
Modify message handling in LLM session
Fix anomalyco#45359 with workaround
brendandebeasi commentedon Oct 6, 2026
There is a second trigger for this beyond
MAX_STEPS_PROMPT: silent safety/policy suppressions where Gemini emits reasoning tokens but zero output tokens.We hit this repeatedly on
gemini-3.8-flashinv1.18.32during code audits and security/auth reviews:output: 0,finish: "unknown").step=1.step=1ends on the empty assistant/reasoning turn. Gemini immediately rejects it with HTTP 400 (Requests ending with a model turn are not supported.).Regarding #51104: blindly appending a synthetic user
Continue.message when the trailing turn is assistant won't fix this scenario. If the provider suppressed the output due to content policy, promptingContinue.will just trigger another empty response or loop.The runner needs to detect when a Gemini turn completes with reasoning tokens but zero candidate output/tools, and surface that the response was withheld (e.g. content/safety filter triggered) instead of cascading into a continuation loop or a 400 error.
rekram1-node commentedon Oct 6, 2026
Shouldn't be possible on v2: https://opencode.ai/v2/docs
Also blocked responses are recognized and halted on, they will never auto continue unless it is a completely custom provider that doesnt send an existing recognized stop reason.