diff --git a/packages/ai/src/protocols/gemini.ts b/packages/ai/src/protocols/gemini.ts
index 3f22a5787e36..b8c1b4b95cd7 100644
--- a/packages/ai/src/protocols/gemini.ts
+++ b/packages/ai/src/protocols/gemini.ts
@@ -289,7 +289,9 @@ const lowerMessages = Effect.fn("Gemini.lowerMessages")(function* (request: LLMR
if (message.role === "system") {
const part = yield* ProviderShared.wrappedSystemUpdate("Gemini", message)
const previous = contents.at(-1)
- if (previous?.role === "user")
+ // Gemini rejects a continuation whose function-response turn carries extra
+ // parts, so an update after a tool result starts its own user turn.
+ if (previous?.role === "user" && !previous.parts.some((item) => "functionResponse" in item))
contents[contents.length - 1] = { role: "user", parts: [...previous.parts, { text: part.text }] }
else contents.push({ role: "user", parts: [{ text: part.text }] })
continue
diff --git a/packages/ai/test/provider/gemini.test.ts b/packages/ai/test/provider/gemini.test.ts
index 60d536736dc0..c1579af7595d 100644
--- a/packages/ai/test/provider/gemini.test.ts
+++ b/packages/ai/test/provider/gemini.test.ts
@@ -139,6 +139,48 @@ describe("Gemini route", () => {
}),
)
+ it.effect("keeps system updates separate from function responses", () =>
+ Effect.gen(function* () {
+ const prepared = yield* compileRequest(
+ LLM.request({
+ model,
+ messages: [
+ Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: { query: "weather" } })]),
+ Message.tool({ id: "call_1", name: "lookup", result: "done", resultType: "text" }),
+ Message.system("Update."),
+ Message.system("Later update."),
+ ],
+ }),
+ )
+
+ expect(prepared.body.contents).toEqual([
+ {
+ role: "model",
+ parts: [{ functionCall: { id: undefined, name: "lookup", args: { query: "weather" } } }],
+ },
+ {
+ role: "user",
+ parts: [
+ {
+ functionResponse: {
+ id: undefined,
+ name: "lookup",
+ response: { name: "lookup", content: "done" },
+ },
+ },
+ ],
+ },
+ {
+ role: "user",
+ parts: [
+ { text: "\nUpdate.\n" },
+ { text: "\nLater update.\n" },
+ ],
+ },
+ ])
+ }),
+ )
+
it.effect("prepares multimodal user input and tool history", () =>
Effect.gen(function* () {
const prepared = yield* compileRequest(