Repository navigation
fix(cursor): fail over on empty-turn completions instead of fabricating clean 200s - #15725
Merged
diegosouzapw merged 2 commits intoOct 9, 2026
Conversation
…ng clean 200s A cursor-agent turn that decodes zero usable signal (no text, thinking, argumented tool call, or token delta) previously finalized into a clean 200 completion. The combo quality gate passes that fabricated shape, so clients received an empty-but-successful response they could not act on. - ctxProducedSignal: shared predicate for whether a StreamCtx saw usable content; a tool call only counts when it carried non-empty arguments (cursor truncates tool calls under load to a bare name with empty args). - The cursor executor errors the stream when driveH2 completes a turn with no produced signal, so the peek applies the dead-before-content gate and combo fails over. - validateQuality: track content-produced in the streaming peek loop and treat read errors with no prior content/terminator as invalid instead of passing through. Includes unit tests covering the empty-turn red case, healthy controls, and the peek failover chain.
Wire ctxProducedSignal into the empty-turn check, resume embedded tool_result XML without an undeclared identifier, and limit the combo read-error failover to Cursor empty-turn and stream-timeout messages. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
diegosouzapw
merged commit Oct 9, 2026
84af297
into
cheaper-inference:release/v3.8.52
31 of 49 checks passed
Collaborator
|
Thanks @patrykkopycinski — a Cursor turn that only ends with turn_ended, or a tool call whose arguments are blank, now fails over instead of a clean 200. The resume path sends tool results that the translator embedded as user-message XML, and the combo gate only treats Cursor's empty-turn and stream-timeout read errors as hop failures. That commit is on your branch, co-authored. This is in the upcoming release. |
This was referenced Oct 19, 2026
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A cursor-agent turn that decodes zero usable signal (no text, thinking, argumented tool call, or token delta — e.g. only a
turn_endedframe) previously finalized into a clean 200 completion. The combo quality gate passes that fabricated empty-success shape, so clients received a successful-looking response with no content they could act on, instead of failing over to another hop.Truncated tool calls make this worse: under load cursor can emit
finish_reason: "tool_calls"with a bare tool name andarguments: ""; treating the bare name as signal let that empty turn finalize cleanly too.Fix
open-sse/executors/cursor.ts: addctxProducedSignal(ctx)— a shared predicate for whether aStreamCtxsaw usable content. A tool call only counts when it carried non-empty arguments. AfterdriveH2completes, the executor errors the stream (cursor-agent completed turn with no usable content) when no signal was produced, instead of callingfinalizeSseStream.open-sse/services/combo/validateQuality.ts: the streaming peek loop now tracks whether any content/terminator was seen; a read error with no prior content is marked invalid so combo fails over, instead of passing through.Tests
tests/unit/cursor-empty-turn-failover-v9.test.ts:turn_endedreports no produced signal