fix(cli): clear stale retry error when agent auto-recovers mid-turn#7681
Conversation
When a transient API error (e.g. UND_ERR_SOCKET: other side closed) occurs during a tool-use loop, handleErrorEvent sets a static pendingRetryErrorItem with a "Press Ctrl+Y to retry" hint. If the stream processor auto-retries the failed call and the agent completes the turn successfully, the error message lingers on screen because the turn-completion path only clears countdown-based errors, not static ones. Additionally, lastPromptErroredRef stays true, causing the turn to be reported as failed (onDeliveryFailed) even though it succeeded. Fix: in the turn-completion success path, if a static retry error is still showing (no active countdown timer), clear it and reset the errored flag. This correctly handles the auto-recovery case without affecting the existing behavior where errors that truly end the turn keep their retry hint visible.
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with a detailed technical analysis. The description names exact code paths ( Direction: aligned — stale error messages after auto-recovery is a real UX papercut, and fixing the delivery-status reporting ( Size: not applicable — single file in Approach: the scope feels right. Nine lines, one Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的 bug,附带详细技术分析。描述中点名了具体代码路径( 方向:对齐——自动恢复后残留错误消息是真实的 UX 问题,修正投递状态报告( 规模:不适用——仅涉及 方案:范围合理。9 行代码,一个 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
Code ReviewIndependent proposal: given the problem (stale static error + wrong delivery status after auto-recovery), I'd add a check at the turn-completion success path: if there's no active countdown timer but a static error item exists, clear it and reset Findings: no critical blockers. The One suggestion (non-blocking): no test covers this specific auto-recovery path. The existing CI Test Evidence
The primary test suite (ubuntu) passed. macOS/Windows tests and integration tests were skipped by CI configuration. No failures. Not verified: real-scenario TUI behavior (this is a CI run; a maintainer can trigger the isolated 中文说明代码审查独立方案: 给定问题(自动恢复后残留静态错误 + 错误的投递状态),我会在轮次完成的成功路径上添加检查:如果没有活跃倒计时但存在静态错误项,清除它并重置 发现: 无关键阻塞项。 一个建议(非阻塞):没有测试覆盖这个特定的自动恢复路径。现有的 CI 测试证据主要测试套件(ubuntu)通过。macOS/Windows 测试和集成测试被 CI 配置跳过。无失败。 未验证:真实场景 TUI 行为(这是 CI 运行;如果自动恢复 UX 对本 PR 重要,维护者可以触发隔离的 — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 4/5 — clean, minimal fix for a real bug; only nit is the missing test for the auto-recovery path. This is a textbook small bugfix. The problem is real and verifiable from the code: The approach matches what I'd have done independently. The The one thing I'd like to see in a follow-up is a unit test for this specific path (set a static error + errored flag before turn completion, assert both are cleared). Not blocking — the existing suite passes and the logic is straightforward enough to verify by reading. 中文说明置信度:4/5 —— 干净、最小化的修复,针对真实 bug;唯一的不足是缺少自动恢复路径的测试。 这是一个教科书式的小型 bugfix。问题真实且可从代码验证: 方案与我独立提出的完全一致。 希望在后续看到一个针对此路径的单元测试(在轮次完成前设置静态错误 + 错误标志,断言两者都被清除)。不阻塞——现有测试套件通过,逻辑足够简单可以通过阅读验证。 — Qwen Code · qwen3.8-max-preview Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
The previous else-if branch cleared pendingRetryErrorItem whenever it was set and no countdown timer was active — including after terminal Error events. Since handleErrorEvent always clears the timer before setting the error item, every fatal API error hit this branch, silently swallowing the error and reporting the turn as delivered. Gate on !lastPromptErroredRef.current so only countdown-originated errors (from successful auto-retry) are cleared; terminal errors remain visible with the Ctrl+Y hint. Add regression tests for both paths.
|
🤖 Addressed the latest review feedback (round 1/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/10 轮)。改动内容与我反驳保留之处如下: Review feedback addressed[Critical] rc:3646549252 — Terminal errors silently swallowedDecision: Implemented. Verified the finding against the code. Fix: Added [Suggestion] rc:3646549262 — No test covers the new branchDecision: Implemented. Added two tests:
Verification
中文说明已处理的审查反馈[Critical] rc:3646549252 — 终端错误被静默吞没决定:已实现。 针对代码验证了该发现。 修复: 在条件中增加了 [Suggestion] rc:3646549262 — 新分支没有测试覆盖决定:已实现。 新增两个测试:
验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🤖 Addressed the latest review feedback (round 2/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/10 轮)。改动内容与我反驳保留之处如下: Review Feedback Summary[rc:3646888065] [Critical] Stale countdown item not cleared on auto-recovery — ImplementedFinding: The Fix: Replaced Test update: Added assertions to the existing "should clear stale countdown error" test to verify:
Conflict notes: No conflicts ( Verification
中文说明审查反馈总结[rc:3646888065] [Critical] 自动恢复时未清除过期的倒计时项 — 已实现发现: 在轮次完成时的 修复: 将 测试更新: 在现有的 "should clear stale countdown error" 测试中添加了断言,验证:
冲突说明: 无冲突( 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.
— qwen3.7-max via Qwen Code /review
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: No action requiredThe latest review round on PR #7681 contains no actionable feedback:
No code changes were made this round. 中文说明无需操作PR #7681 的最新审查轮次中没有需要处理的反馈:
本轮未进行任何代码更改。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
doudouOUC
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
Local verification report — PR #7681 (maintainer review)Verified head Verdict: safe to merge — no regressions found — but two things should be corrected first. The code change is correct and the 1. What the final diff actually doesAfter three commits, the net change is one branch at the turn-completion path: if (retryCountdownTimerRef.current) {
clearRetryCountdown();
} else if (pendingRetryErrorItemRef.current && !lastPromptErroredRef.current) {
clearRetryCountdown(); // ← new
}The 2. A/B unit tests — the new test is load-bearing
3. Mutation matrix
M1 is the finding. To confirm the shipped behaviour is nonetheless right, I wrote a corrected version of the same test that stubs
So: the gate is correct and necessary — but CI will stay green if someone deletes it. That is worth fixing, since removing the gate silently swallows every fatal API error and reports the turn as delivered (exactly the round-1 Critical). 4. Live TUI verificationBuilt CLI driven through a real PTY ( Scenario A — rate-limit auto-recoveryAttempt 1 returns provider throttling as stream content (
Base and PR are identical: the stale error does not reproduce on base here. Reason, captured live by an instrumented build:
Scenario B — terminal error and mid-tool-loop failureI also reproduced the exact error shape from the PR description ( Branch-reachability probe (instrumented PR build, all four scenarios)
5. PR description is stale relative to the final code
6. Suggested changes before merge
EnvironmentmacOS 15 (Darwin 24.6.0), Node 22.23.1. Both worktrees built from source; 中文说明本地验证报告 —— PR #7681(维护者复核)在两个独立 worktree 中验证了 head 结论:可以合并,未发现回归 —— 但建议先修正两处问题。 代码改动本身是正确的, 1. 最终 diff 的实际内容三个 commit 之后,净改动是 turn-completion 路径上的一个分支: if (retryCountdownTimerRef.current) {
clearRetryCountdown();
} else if (pendingRetryErrorItemRef.current && !lastPromptErroredRef.current) {
clearRetryCountdown(); // ← 新增
}第一个 commit 中的 2. A/B 单元测试 —— 新测试确实有效测试文件是纯新增(105 行插入、0 行删除),因此我把它原样复制到 base worktree,两侧跑同样的测试。
3. 变异矩阵
M1 就是核心问题。 为确认线上行为本身没问题,我写了一个修正版测试(补上
即:门控是正确且必要的,但如果有人删掉它,CI 依然是绿的。 这值得修复,因为删掉门控会导致所有致命 API 错误被静默吞掉并把该轮报告为已送达(正是第 1 轮 Critical 指出的问题)。 4. 实机 TUI 验证用真实 PTY( 场景 A —— 限流自动恢复
base 与 PR 表现完全一致:该问题在 base 上并未复现。 插桩构建实机抓到的原因:
场景 B —— 终止错误与工具循环中途失败我还复现了 PR 描述中提到的确切错误形态( 分支可达性探针(插桩 PR 构建,四个场景)
5. PR 描述已与最终代码脱节
6. 合并前建议
环境macOS 15(Darwin 24.6.0),Node 22.23.1。两个 worktree 均从源码构建; |
|
🤖 Addressed the latest review feedback (round 3/10). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/10 轮)。改动内容与我反驳保留之处如下: Review feedback addressed1. Fix the vacuous terminal-error test (§6.1) — ImplementedThe Fix: Added Mutation verification: Confirmed that with this fix, removing the 2. Rewrite PR description to match final diff (§6.2) — ImplementedUpdated 3. Consistency of silent error clearing (§6.3) — DeclinedThe branch is a defensive safety net that current core never reaches (the follow-up Verification
中文说明已处理的评审反馈1. 修复空转的终止错误测试(§6.1)— 已实现
修复: 在基础 变异验证: 确认加上此修复后,移除 2. 重写 PR 描述以匹配最终 diff(§6.2)— 已实现更新了 3. 静默清理错误的一致性(§6.3)— 已拒绝该分支是防御性兜底,当前 core 永远不会触达(后续 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 10 this bot stops and leaves the PR for a human. · 有空请复审;第 10 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
…ercises turn-completion path (#7681)


What this PR does
Clears the "Press Ctrl+Y to retry" error message from the screen when the agent auto-recovers from a transient API error mid-turn. Also resets the
lastPromptErroredRefflag so the turn is correctly reported as delivered instead of failed.Why it's needed
When a transient API error (e.g.
UND_ERR_SOCKET: other side closed) occurs during a tool-use loop,handleErrorEventsets a staticpendingRetryErrorItemwith the error text and a "Press Ctrl+Y to retry" hint. If the stream processor auto-retries the failed API call and the agent completes the turn successfully, two things go wrong:Error message lingers: the turn-completion path at line ~2943 only clears countdown-based errors (
retryCountdownTimerRef.current), not static errors. The comment explicitly says "Do NOT clear static error+hint" — but that design assumed errors always end the turn, which is false when auto-recovery happens within a tool-use loop.Turn reported as failed:
lastPromptErroredRef.currentstaystrue, sometadata?.onDeliveryFailed?.()fires instead ofonDelivered(), even though the turn completed successfully.Ctrl+Y is useless: the user sees the retry hint but pressing Ctrl+Y does nothing because
streamingStateisResponding(the agent is still running), andretryLastPromptsilently returns in that state.Reviewer Test Plan
How to verify
npm run buildfollowed by more work).Evidence (Before & After)
Before: error message
[API Error: terminated (cause: UND_ERR_SOCKET: other side closed)] (Press Ctrl+Y to retry)persists on screen while agent continues working.After: error message is cleared when the turn completes successfully.
Tested on
Environment (optional)
Unit tests: 162/162 passed in
useGeminiStream.test.tsx.Risk & Scope
Linked Issues
N/A
中文说明
修复 agent 在 tool-use 循环中遇到瞬时 API 断连后自动恢复时,错误消息残留在屏幕上的问题。同时修正 lastPromptErroredRef 标志,使成功恢复的轮次正确报告为 delivered 而非 failed。