fix(cli): retry responses without finish reasons#12157
Conversation
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Code Review SummaryStatus: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (6 files)
Fix these issues in Kilo Cloud Previous Review Summaries (2 snapshots)Current summary above is authoritative. Previous snapshots are kept for context only. Previous reviewStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (3 files)
Fix these issues in Kilo Cloud Previous review (commit 833875f)Status: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (5 files)
Reviewed by gpt-5.6-sol · Input: 89K · Output: 7.9K · Cached: 579.1K Review guidance: REVIEW.md from base branch |
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
…ish-reason # Conflicts: # packages/opencode/test/kilocode/session-processor-retry-limit.test.ts
| ) { | ||
| if (msg.finish) return Effect.void | ||
| msg.finish = "unknown" | ||
| if (step.reasoning || step.text || step.tool) return Effect.void |
There was a problem hiding this comment.
WARNING: Incomplete tool-input framing suppresses the retry
step.tool is set by tool-input-start, before a complete tool call or executable input exists. If the provider disconnects after that start event, this condition accepts the response as partial output instead of retrying it; cleanup then records the pending call as Tool execution aborted, so the user receives a failed phantom tool call rather than the intended missing-finish recovery. Consider marking tool output observable only once a complete tool-call has arrived.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| Stream.takeUntil(() => ctx.needsCompaction), | ||
| Stream.runDrain, | ||
| ) | ||
| yield* KiloSessionProcessor.ensureFinish(ctx.assistantMessage, ctx.step) |
There was a problem hiding this comment.
WARNING: Retried framing leaves stale persisted parts behind
An empty response can still persist step-start, empty/whitespace text or reasoning parts, and partial tool-input state before this failure triggers. The next attempt clears only the in-memory references, so prior text/reasoning parts can remain without end timestamps and each retry adds another set of fragments; pending tool calls also survive until final cleanup. Please settle or remove all attempt-local parts and tool state at this retry boundary before replaying the request.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| // kilocode_change start — enforce retry limit | ||
| if (opts.limit !== undefined && meta.attempt > opts.limit) { | ||
| const allowed = opts.allow?.(meta.input) | ||
| if (allowed === false || (allowed !== true && opts.limit !== undefined && meta.attempt > opts.limit)) { |
There was a problem hiding this comment.
WARNING: Dedicated retries still consume the provider retry budget
Although allowed === true bypasses the limit check for MissingFinishError, Effect's shared meta.attempt still increments. A missing-finish retry followed by retryable provider errors therefore reaches opts.limit early; with limit 2, one missing-finish response leaves only one provider retry before the next 429 is stopped. This conflicts with the intended independent caps. Track general-provider attempts separately from specially allowed missing-finish attempts.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
What changed
Treat model streams that end without a finish reason as retryable and retry them three times before surfacing the existing incomplete-response error.
The dedicated cap is independent of the general provider retry limit, so this recovery path always gets three retries but cannot loop indefinitely. Successful retry attempts clear the prior unknown finish marker before processing the next stream.
Why
These streams complete without throwing, so the existing provider error retry policy never handled them. In the VS Code extension they appeared immediately as
Response ended without a finish reason and may be incomplete.instead of being retried.