Skip to content

Gemini: "Requests ending with a model turn are not supported" when a step is stored out of order (IDs are not monotonic across backward clock steps) #15073

Open

Description

@piercypixel

Summary

Kilo VS Code extension / CLI 7.8.8, Google provider (@ai-sdk/google, default ai-sdk runtime), gemini-3.8-flash. In the middle of a tool-calling turn a step fails with:

AI_APICallError: Requests ending with a model turn are not supported.   (HTTP 400 INVALID_ARGUMENT)

isRetryable is false, so the turn hard-stops and the user has to type a new prompt (for example "continue") to resume. On a host whose wall clock steps backward (WSL2 here, but NTP corrections and VM resume do the same elsewhere) it happens several times a day: the local kilo.db holds 74 occurrences across 24 sessions.

Root cause

  1. Identifier.create (packages/opencode/src/id/id.ts) builds IDs from Date.now(). The "monotonic" state only handles two IDs minted in the same millisecond; nothing stops a later ID from sorting before an earlier one when the clock steps backward in between.
  2. Parts are stored and replayed in ID order (MessageV2.toModelMessagesEffect reads PartTable ordered by id).
  3. A Gemini step writes its parts as step-start > reasoning > tool. A backward step of a few hundred milliseconds between step-start and the tool call stores the step as tool > step-start > reasoning (or tool > step-finish > step-start > reasoning when the whole step was short).
  4. The AI SDK's convertToModelMessages starts a new block at every step-start, so that message replays as assistant(tool-call) > tool(result) > assistant(reasoning).
  5. @ai-sdk/google emits the trailing reasoning-only block as a model turn (thought: true), and Gemini rejects any request whose contents end with a model turn.

Stored parts of the step right before one failure (UTC, listed in ID order, which is also time_created order):

msg_13bb9f89400124loVqN16mcfHL  role=assistant finish=tool-calls
  prt_13bba0cca001…  18:43:31.146  tool        bash, completed          <- minted before its own step-start
  prt_13bba0e0a001…  18:43:31.466  step-start
  prt_13bba0e0c001…  18:43:31.468  reasoning   time.start=…411468  time.end=…411302   <- end is before start: the clock stepped back
  prt_13bba11a6001…  18:43:32.391  step-finish reason=tool-calls
msg_13bba12cf001hH63Waabr4oSPP  role=assistant error="Requests ending with a model turn are not supported." (400)

The chat UI shows the same thing: for the broken step the "Shell" block renders above its own "Reasoning" block, while every healthy step renders reasoning first.

Of the 74 stored occurrences, 72 directly follow a step whose tool part sorts before its own step-start. The remaining 2 followed an auto-compaction continuation whose summary assistant message was the last message in the replay (same wire shape, different trigger).

Over the last 3 days on this host, between 0.5 % and 1.5 % of steps for every provider in use (openai, xai, kimi, google) are stored with a tool or reasoning part ahead of the first step-start. Only Gemini fails loudly. The other providers silently receive a transcript in which the step's reasoning is replayed after its tool result.

Live confirmation

Replaying the shape above against gemini-3.8-flash through @ai-sdk/google:

control: ends with tool result     [user > assistant > tool]             -> 200
misordered replay, unguarded       [user > assistant > tool > assistant] -> 400 Requests ending with a model turn are not supported.
misordered replay, guarded         [user > assistant > tool]             -> 200   (trailing reasoning-only assistant dropped)
text tail, unguarded               [user > assistant]                    -> 400 Requests ending with a model turn are not supported.
text tail, guarded                 [user > assistant > user]             -> 200   ("Continue." primed)

Related upstream reports

Fix

Two layers, in the linked PR:

  1. Identifier.create keeps wall-clock IDs strictly ascending across a backward clock step, so new steps can no longer be stored out of order.
  2. ProviderTransform.message never lets a Gemini request end on a model turn: a trailing reasoning-only assistant message is dropped and any other assistant tail gets a primed user turn. Outbound only, so sessions that already contain a misordered step (and the compaction-continuation trigger) recover without touching the stored transcript.

Not covered: messages are ordered by time_created and that column is still the raw wall clock, so a backward step can still reorder whole messages (each still ends on a tool result, so Gemini accepts it). A repair for already-stored misordered steps on replay is also possible but left out of the fix.

Activity

  1. added
    communityOpened by someone outside the Kilo team
    needs-triageCommunity PR no maintainer has looked at yet
    on Oct 14, 2026
  2. piercypixel commented on Oct 14, 2026

    @piercypixel
    Author

    Filed upstream, where the shared files live: issue anomalyco/opencode#55514 and PR anomalyco/opencode#55515 against v1. Closed #15074 to avoid carrying a kilocode_change copy. Once the upstream PR merges, the next upstream merge into packages/opencode picks it up and this issue can be closed. Branch piercypixel/kilocode:fix/monotonic-ids-gemini-model-turn still holds the port if it is wanted sooner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    communityOpened by someone outside the Kilo teamneeds-triageCommunity PR no maintainer has looked at yet

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions