Skip to content

[Provider compatibility] Cursor: incomplete tool-call streams poison conversation reuse #4874

Closed

Description

@MerryEcho

Client or integration

Codex App

Provider or upstream service

cursor

OpenCodex version

2.57.0

Endpoint or capability

/v1/responses Cursor protobuf tool calls

Current behaviour

On Codex Desktop through OpenCodex, Cursor models (including cursor/grok-4.6) talk Connect protobuf, not Responses. When a Cursor stream ends with a client tool still open, OpenCodex fail-closes:

Cursor stream ended with incomplete tool call(s)

That error is streamed. It is not thrown, so the overflow/invalid_argument remint ladder never runs. _cursorConversationId is assigned at the start of runTurn and persisted by continuationStateForResponse. The next turn reuses that id (or the deterministic thread hash). Cursor's side of the conversation is still waiting for mcpResult, so later turns in the same thread interrupt more often. Neighboring threads that never interrupted stay clean.

Native Composer replay also flushes unpaired toolCallStep history without a result, which looks like an interrupted attempt. External wire models such as grok-4.6 skip native mcpToolCall replay, so history repair alone cannot recover them; conversation remint is the path.

This is not a Cursor 429 / Ultra quota issue. The incomplete-tool event is a truncated stream, and CursorStreamTruncatedError is defined but never thrown.

Expected behaviour

Keep fail-closed on the current turn: do not emit a partial tool_call_start, and do not retry that send. After the incomplete-tool error is streamed, eligible non-isolated turns should remint the Cursor conversation id (same helper as overflow remint), persist the thread override for store:false clients, and invalidate the inherited checkpoint so the next turn does not resume a conversation left waiting for mcpResult. Isolated helper/compaction turns must not remint or donate that recovery to the parent.

Native Composer unpaired toolCallStep history should carry an honest [missing tool_result for this tool_use in history] placeholder (isError: true), matching Google #2199. External grok-4.6 stays on conversation remint.

Minimal redacted request or reproduction

Codex App -> local OpenCodex proxy -> cursor/grok-4.6 (or another Cursor model). Run a tool-using thread until the proxy reports Cursor stream ended with incomplete tool call(s), then continue the same thread. Later turns in that thread interrupt more often than a neighboring thread that never had a truncated tool stream.

Actual response or error

Cursor stream ended with incomplete tool call(s): <call id>. Arguments may be truncated; the call was not committed.
Subsequent turns in the same thread reuse the same Cursor conversation id and interrupt again.

Upstream documentation

No public Cursor Connect specification. The existing OpenCodex fail-closed path is finalizeTurnEvents in src/adapters/cursor/protobuf-events.ts. Overflow remint is the in-tree recovery helper this should reuse. Related repair: #2199 (Google missing tool_result). Distinct from xAI Responses adjacency (#4870 / #4871).

Suggested mapping or implementation notes

  • Add isCursorIncompleteToolCallMessage and flag that streamed error in src/adapters/cursor.ts.
  • After runOnce returns, remint via remintConversationId for non-isolated turns. Do not retry the current send.
  • Native conversationTurns flush: unpaired pendingToolCalls get toolCallStep(..., missingToolResult, codeMode).
  • Do not classify this as 429. Quota/rate cues stay on the existing overflow/rate path.

Additional context and attachments

Related: #2199, #3506, #1527, #4245, #4870 (xAI Responses, different adapter). Cursor 429 / empty-spin is a different failure and is not requested here.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Activity

  1. added
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 17, 2026
  2. Ingwannu commented on Sep 17, 2026

    @Ingwannu
    Owner

    Confirmed on current dev (7868f5df5). finalizeTurnEvents() streams the incomplete-tool error, while src/adapters/cursor.ts only remints on the existing thrown overflow/cancel paths. Because the incomplete case is returned as an event, the conversation id and inherited checkpoint remain reusable on the next turn. Native replay also flushes pending tool calls without an honest missing-result placeholder.

    Draft PR #4875 addresses the two distinct boundaries without retrying the failed send: eligible non-isolated turns remint after the streamed incomplete-tool error and invalidate inherited checkpoint state, while native Composer history records an explicit missing tool_result. Isolated helper/compaction requests remain fail-closed and do not donate recovery to the parent. Keeping this issue open until the PR is review-ready, its exact-head hosted CI is green, and the current head is approved.

  3. lidge-jun commented on Sep 17, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 28 / 80

    이 이슈는 Cursor Connect protobuf 경로에서 스트림이 열린 tool call을 남긴 채 끝나면 OpenCodex가 "Cursor stream ended with incomplete tool call(s)"를 흘리고, _cursorConversationId가 재사용되어 같은 스레드가 계속 깨진다는 내용이다. 보고 버전은 2.57.0, Cursor/grok-4.6을 포함한 Desktop 경로다.

    현재 tip 41f1832(package 2.58.0)에는 이미 #4875 fix(cursor): remint conversation after incomplete tool-call streams (ee28833)가 들어가 있다. src/adapters/cursor.ts에 remintConversationId와 incomplete-tool-remint / incomplete-tool-remint-exhausted 진단이 보인다. 본문이 요구한 "overflow/invalid_argument remint ladder가 스트림 에러에는 안 돈다 → conversation remint"가 tip에 반영된 상태다.

    이슈가 열린 채로 남은 이유는 merge 커밋에 Closes #4874가 없었거나 자동 종결이 안 된 것으로 보인다. 타임라인에는 #4875 cross-ref와 커밋 referenced만 있다. 인접 draft #4900(grok-4.6 tool-result echo)은 다른 잔여 증상이면 그쪽으로 이어가면 된다.

    tip 기준 본문 재현 경로는 수정된 상태로 보는 것이 맞다. 2.57.0 보고 → tip 2.58.0+#4875. 재발을 보려면 tip SHA와 incomplete-tool-remint 로그를 새 이슈에 붙이면 된다.

    라인 432 - src/adapters/cursor.ts remintConversationId
    라인 553 - src/adapters/cursor.ts incomplete-tool-remint 진단
    경로/심볼 - ee28833 / #4875: tip 랜딩. 이 이슈 본문 픽스
    경로/심볼 - #4900: tip 이후 Cursor grok-4.6 echo 잔여가 있으면 별도 추적

    메인테이너의 판단이 필요한 지점

    • tip #4875만으로 보고 시나리오가 충분한지
    • #4900을 후속으로 필수 묶을지
    • 자동 close 누락을 수동 close 멘트로 정리할지

    너의 추천
    Fixed by #4875 (ee28833) on dev @41f1832f6 로 close. 잔여 echo는 #4900만 본다. 라벨은 바꾸지 않는다.

    이 댓글은 grok-bot이 작성했습니다

  4. lidge-jun commented on Sep 17, 2026

    @lidge-jun
    Owner

    Fixed on dev by #4875 (ee28833160), released in 2.58.0.

    After an incomplete tool-call stream the conversation is reminted, the thread override is set, and the stale checkpoint is invalidated — with a bounded counter, because an unbounded remint on a model that fails every turn is worse than the original symptom. The current send is not retried and isolated turns are excluded, which keeps this to preventing new contamination rather than silently replaying work.

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

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions