fix: preserve tool messages in history to prevent Gemini ordering errors#251
fix: preserve tool messages in history to prevent Gemini ordering errors#251elpiarthera wants to merge 1 commit intoget-convex:mainfrom
Conversation
docsToModelMessages filtered out messages with empty content, which could drop tool-role messages after orphan filtering. This breaks Gemini's strict functionCall -> functionResponse ordering requirement. Also adds console.warn when filterOutOrphanedToolMessages drops tool messages, making it easier to diagnose missing tool results. Fixes get-convex#200
| .map((m) => m.message) | ||
| .filter((m) => !!m) | ||
| .filter((m) => !!m.content.length) | ||
| .filter((m) => m.role === "tool" || !!m.content.length) |
There was a problem hiding this comment.
why are we passing through all tools here just to filter them out elsewhere?
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughChanges add logging when tool messages are dropped in search filtering and modify message retention logic to preserve tool-role messages with empty content, addressing Gemini model validation failures caused by corrupted tool call history. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Re: @ianmacartney comment on line 138 — good point. The intent was defensive: if a tool message ends up with empty content after filterOutOrphanedToolMessages partially strips it, the old filter would drop it entirely, breaking Gemini's strict functionCall → functionResponse ordering. But you're right that the better fix is upstream — filterOutOrphanedToolMessages should either keep the full tool message or drop it cleanly (not leave an empty shell). The console.warn in this PR helps diagnose when that happens. If you'd prefer, I can move the fix to filterOutOrphanedToolMessages instead — make it either keep all parts of a tool message or drop the entire message (no partial stripping). That way docsToModelMessages doesn't need the special case. Let me know which approach you prefer. Orchestrator: Zeta — VantageOS Team Dev | 2026-04-10 |
|
Good thinking. Moving it to `filterOutOrphanedToolMessages` sounds right to
me
…On Fri, Apr 10, 2026 at 1:44 AM elpiarthera ***@***.***> wrote:
*elpiarthera* left a comment (get-convex/agent#251)
<#251 (comment)>
Re: @ianmacartney <https://github.com/ianmacartney> comment on line 138 —
good point.
The intent was defensive: if a tool message ends up with empty content
after filterOutOrphanedToolMessages partially strips it, the old filter
would drop it entirely, breaking Gemini's strict functionCall →
functionResponse ordering.
But you're right that the better fix is upstream —
filterOutOrphanedToolMessages should either keep the full tool message or
drop it cleanly (not leave an empty shell). The console.warn in this PR
helps diagnose when that happens.
If you'd prefer, I can move the fix to filterOutOrphanedToolMessages
instead — make it either keep all parts of a tool message or drop the
entire message (no partial stripping). That way docsToModelMessages doesn't
need the special case. Let me know which approach you prefer.
Orchestrator: Zeta — VantageOS Team Dev | 2026-04-10
—
Reply to this email directly, view it on GitHub
<#251 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACZQWZRCB6A322NIGA4QDL4VCX5XAVCNFSM6AAAAACXS6KD6OVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DEMRSGMYDEOBVG4>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Summary
docsToModelMessagesto preserve tool-role messages even when content appears empty — prevents Gemini's strictfunctionCall → functionResponseordering from breakingconsole.warninfilterOutOrphanedToolMessageswhen tool messages are dropped, logging the message ID, its toolCallIds, and available toolCallIds for diagnosticsRoot cause
When tool messages have empty content after orphan filtering,
docsToModelMessagesfiltered them out via.filter(m => !!m.content.length). This left gaps in the conversation history (missingfunctionResponseafterfunctionCall), which Gemini rejects with "Please ensure that function call turn comes immediately after a user turn or after a function response turn."Test plan
npm run buildpassesnpm run lintpasses (0 errors)npm testpasses (262/262)Fixes #200
Orchestrator: Zeta — VantageOS Team Dev | 2026-04-09
Summary by CodeRabbit