fix(coderd/x): surface Gemini malformed-function-call stream deaths as retryable errors - #28470
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 28b3ab53a7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c591b3f7a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
30b2bb0 to
fb8e805
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Problem
When Gemini's OpenAI-compatible endpoint rejects a model-generated function call server-side, it ends the SSE stream cleanly with the nonstandard finish reason
function_call_filter: MALFORMED_FUNCTION_CALLafter streaming only thought summaries; the rejected call never reaches the wire. chatd treated this as a normal completion: fantasy maps the unrecognized finish reason tounknown, the reasoning block never closes (the only non-thought delta is the</thought>marker, which the transport seam strips to an empty string, and the openaicompat hook only ends reasoning on a non-empty content delta), so the step accumulates no content and the generation loop finishes the turn as complete. The user sees the model think for ~40 seconds and then nothing: no assistant message, nolast_error, chat statuswaiting. Observed twice in a row in production on gemini-3.7-flash, with "Resume" reproducing it identically.Fix
Two independent layers:
coderd/x/googleopenai: the stream rewrite now converts any chunk whosefinish_reasonstarts withfunction_call_filterinto an OpenAI-style SSE{"error": ...}event embedding the raw reason. openai-go turns error-bearing events into stream errors, so the failure rides the existing stream-error path instead of ending the stream cleanly.coderd/x/chatd/chaterror: classifies that injected error as retryable (kindgeneric, providergoogle) with a clear user-facing message, so the existing generation retry machinery re-runs the step and persists alast_errorif retries exhaust.coderd/x/chatd/chatloop: provider-agnostic guard: a step that produced no user-visible content and no tool calls under a finish reason ofunknown,error,other, ortool-calls(a tool-calls finish that delivered zero calls) now returns a retryable error instead of silently completing the turn.stopandlengthfinishes keep their existing semantics.Tests cover the seam rewrite (live-capture SSE shape plus standard finish reason passthrough), the new classification, and the chatloop guard (error cases plus preserved stop, length, text, and tool-call behavior). Each layer was red-green verified independently.
Remote dogfood UAT ran against this exact commit: normal reasoning and tool-call chats on a real model complete cleanly with no spurious guard errors and no retry loops. The Google-side failure itself is not deterministically triggerable against live Gemini and is owned by the unit tests.