fix(cli): soften update-check failure UX — warning instead of error, raise timeout to 5s - #7409
Conversation
…enLM#7049) The startup update check now surfaces failures as a soft warning with the concrete failure reason, instead of an alarming red error: - Raise FETCH_TIMEOUT_MS from 2s to 5s, matching comparable CLIs, so slow mirrors and corporate proxies stop tripping the check. - Emit startup check failures on 'update-failed' with severity 'warning'; setUpdateHandler renders them as a yellow warning. Actual update install failures keep error severity. - Add classifyUpdateCheckError/describeUpdateCheckFailure so messages say what happened - timeout, unreachable registry, or registry error, e.g. "Update check skipped (registry unreachable) - run /update to retry." - /update keeps loud error styling but now includes the failure reason. - Update en/zh/zh-TW locales; drop the now-unused generic failure key. This is not a revert of QwenLM#6857/QwenLM#6887 - the loud-failure direction stays. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the PR! Template looks good ✓ — headings are all present. The "Tested on" table and "Evidence" sections are unfilled, but the top description and linked issue provide enough context. Problem: observed UX issue with solid evidence. Issue #7049 documents users on slow/blocked registries seeing an alarming red Direction: aligned. This is a UX refinement of how update-check failures are surfaced, explicitly not a revert of #6857/#6887 — the loud-failure direction stays. The issue was opened by a collaborator and triaged as "community PRs welcome." CHANGELOG: no direct reference, but the area (update check UX) is clearly relevant. Size: not applicable — all changes are in Approach: scope feels right — the PR maps 1:1 to the three asks in #7049 (raise timeout, soften severity, differentiate failure reason). No drive-by refactors or unrelated changes. One note: the old generic i18n key is dropped from ca/de/fr/ja/pt/ru but the new parameterized keys are only added to en/zh/zh-TW, so those six locales will fall back to English until translated. Not a blocker — just worth knowing. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ — 所有标题均存在。"Tested on"表格和"Evidence"部分未填写,但顶部描述和关联 issue 提供了足够的上下文。 问题:已观测到的 UX 问题,有充分证据。Issue #7049 记录了慢速/被屏蔽 registry 的用户每次启动时看到红色 方向:对齐。这是对更新检查失败展示方式的 UX 优化,明确不是 #6857/#6887 的回退——失败可见性保持不变。Issue 由 collaborator 开启,triage 为"欢迎社区 PR"。 规模:不适用——所有改动在 方案:范围合理——PR 与 #7049 的三个要求一一对应(提高超时、降低严重性、区分失败原因)。无顺手重构或无关改动。注意:旧的通用 i18n key 从 ca/de/fr/ja/pt/ru 中移除,但新的参数化 key 仅添加到 en/zh/zh-TW,这六个语言将回退到英文,直到翻译完成。不是阻塞项。 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewIndependent proposal: Given #7049, I'd (1) raise Comparison: The PR matches this almost exactly. The implementation is clean:
No critical blockers or AGENTS.md violations found. 134 unit tests pass across all 6 changed test files. One minor note: the old generic i18n key is dropped from ca/de/fr/ja/pt/ru but the new parameterized keys are only added to en/zh/zh-TW — those six locales fall back to English. Not a blocker. Before (installed qwen v0.20.0, unreachable registry)Startup update check — red
After (this PR, unreachable registry)Startup update check — yellow
The before/after confirms the PR delivers exactly what #7049 asked for: the alarming red 中文说明代码审查独立方案: 针对 #7049,我会 (1) 将 对比: PR 几乎完全匹配。实现干净:
无关键阻塞项或 AGENTS.md 违规。134 个单元测试全部通过。 测试证据Before:红色 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 4/5 — clean, well-scoped fix that delivers exactly what #7049 asked for; only nit is the i18n fallback for six locales. Stepping back: this PR solves a real, user-reported problem (red ✕ error on every startup for users on slow registries), the implementation is straightforward (two small functions + a severity field), every change in the diff maps directly to one of the three asks in the issue, and the before/after tmux output confirms it works as promised. The code is the kind I'd thank the author for in six months — clear naming, good comments where the why matters, comprehensive tests. The only reservation is minor: ca/de/fr/ja/pt/ru lose their translated error message and fall back to English until someone adds the new parameterized keys. Not worth blocking over. Approving. ✅ 中文说明置信度:4/5 — 干净、范围合理的修复,完全实现了 #7049 的要求;唯一的小问题是六个语言的 i18n 回退。 总结:这个 PR 解决了一个真实的用户报告问题(慢速 registry 用户每次启动看到红色 ✕ 错误),实现简单(两个小函数 + 一个 severity 字段),diff 中的每个改动都直接对应 issue 的三个要求之一,before/after tmux 输出确认了效果。代码清晰,命名好,注释到位,测试全面。 唯一的保留意见很小:ca/de/fr/ja/pt/ru 失去了翻译的错误消息,回退到英文。不值得阻塞。 批准。✅ — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
Review follow-up: the previous commit removed the old generic failure key from ca/de/fr/ja/pt/ru but only added the new parameterized keys to en/zh/zh-TW, leaving those six locales falling back to English. Add the translations, reusing each locale's existing registry/update terminology. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the review! Follow-up pushed:
|
|
@qwen-code /triage |
yiliang114
left a comment
There was a problem hiding this comment.
Thanks for working on this. The 5s timeout and turning the startup failure into a warning address the reported problem. The current diff goes beyond that by introducing a three-way error taxonomy and threading it through qwen update, /update, and the relaunch path. Could we narrow this PR to the startup check only, and leave the explicit update flows unchanged? That would make the behavior easier to verify and keep #7049 focused. I left a few concrete comments inline. Please also fill in the reviewer test plan/risk sections and use Fixes #7049.
| writeStderrLine( | ||
| t( | ||
| 'Failed to check for updates. Please check your network or registry configuration.', | ||
| 'Failed to check for updates ({{reason}}). Please check your network or registry configuration.', |
There was a problem hiding this comment.
The report in #7049 is about the non-blocking startup check. This changes the explicit qwen update failure contract as well, and the same propagation appears in /update and the relaunch path. Could we leave those paths unchanged in this PR and keep the change at the startup boundary?
| * global-npm path surfaces network failures only through `npm` child-process | ||
| * stderr embedded in the error message. Related: #7049. | ||
| */ | ||
| export function classifyUpdateCheckError( |
There was a problem hiding this comment.
If we keep a specific failure reason for startup, could we limit it to UpdateCheckTimeoutError plus a generic fallback? The real errors do not reliably match this taxonomy: Node 22 fetch puts ENOTFOUND under error.cause, while execFile({ timeout }) returns code: null, killed: true, and signal: SIGTERM. Both currently become registry error.
|
|
||
| describe('timeout budget (#7049)', () => { | ||
| it('allows at least 5 seconds for slow registries', () => { | ||
| expect(FETCH_TIMEOUT_MS).toBeGreaterThanOrEqual(5000); |
There was a problem hiding this comment.
This should be toBe(5000). >= 5000 would still pass if this accidentally became 50 seconds, and the other timeout tests advance by the same constant, so they would not catch that regression either.
macOS Apple Silicon verification (2026-07-21)Tested commit
Passed
Finding: nested network errors are classified as
|
Code review — update-check failure UX (#7049)Reviewed the full diff. This is a well-scoped, well-tested change. It does exactly what it says: softens the startup background check to a yellow What it does
Correctness — verified
Optional nits (none blocking)
Risk & testsLow risk. The one real hazard — downgrading genuine failures to warnings — is contained by the Overall: LGTM. The nits above are optional and can be follow-ups. 🤖 Reviewed with Claude Code · model: Claude Opus 4.8 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
yiliang114
left a comment
There was a problem hiding this comment.
The core here — softening the startup check to a warning and raising the fetch budget to 5s — is right, and it doesn't regress anything: worst case the message stays as generic as it was before. Clearing the earlier change request.
Two things aren't fully solved, but neither blocks this, so tracking them as a follow-up in #7423:
- The timeout/offline taxonomy doesn't match the real error shapes it's meant to catch, so both degrade to the generic "registry error" (repro in the issue).
- Scope: the
qwen update/ relaunch failure contract changed beyond what #7049 needs.
LGTM.
ZijianZhang989
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
Address review feedback from QwenLM#7409: - Revert the reason threading through 'qwen update', '/update', and update-relaunch — the explicit update flows keep their original failure contract; the change now sits only at the startup boundary. - Drop the three-way error taxonomy. Real failures do not reliably match it: Node 22 fetch nests ENOTFOUND under error.cause and execFile with a timeout reports killed/SIGTERM with code null, so both fell through to the generic bucket anyway. Startup now distinguishes only our own UpdateCheckTimeoutError (specific reason) from everything else (generic 'Update check skipped — run /update to retry.'). - Pin the timeout guard test to toBe(5000). - Restore the original generic failure key in all locales and translate the two startup skipped messages in all nine locales. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Released in v0.20.1. |
What this PR does
Softens how startup update-check failures are presented and gives the check a realistic time budget. On networks where the npm registry is slow or unreachable, Qwen Code used to greet the user with a red
✕ Failed to check for updates. Please check your network or registry configuration.on every launch — alarming wording for something that is a non-blocking background convenience. With this PR the same situation renders as a calm yellow warning that says what actually happened and how to retry:△ Update check skipped (registry unreachable) — run /update to retry.Three coordinated changes deliver this:
npm outdatedon Windows consistently takes ~3s). 5s matches comparable CLIs — Claude Code's autoUpdater usesAbortSignal.timeout(5000). The constant is shared by the update-notifier fetch path and the global-npmnpm viewchild-process path, so both get the new budget.update-failedevent payload gains an optionalseverity?: 'error' | 'warning'field, defaulting to'error'so every existing emitter is unaffected. Only the startup background check emitsseverity: 'warning', which the UI renders as a yellow△warning. Genuine update install failures keep the red✕error, and/update— an explicit user action — also keeps error styling.ENOTFOUND/ECONNREFUSED/EAI_AGAIN/ETIMEDOUT/ENETUNREACH, both onerror.codeand embedded in npm child-process stderr text), and registry (anything else). All four call sites — startup check,/updateslash command,qwen updateCLI, and the sandbox update-relaunch path — now include the concrete reason in their message.i18n: the new strings are translated in all nine locales (en, zh, zh-TW, ca, de, fr, ja, pt, ru), reusing each locale's existing registry/update terminology; the now-unused generic key is removed everywhere.
npm run check-i18npasses.Why it's needed
Users on slow or blocked registries (some regions, corporate proxies) see a red error on every startup even though the app works normally — issue #7049, with a real-world downstream report in #7044 (China, v0.19.11, "升级就报错"). The failure surfacing itself is correct and recent (#6857 fixed a real false-negative bug, shipped via #6887); what's wrong is the presentation: error-red styling and a generic message for a background check that the user never asked for and that doesn't block anything.
This is explicitly not a revert of #6857 / #6887. Failures stay visible ("loud failure"); only the presentation, wording, and time budget change.
Reviewer Test Plan
How to verify
Unreachable registry → offline path (startup, TUI):
Unreachable registry → explicit
/updatekeeps loud error, now with the reason:Timeout path (slow-but-reachable registry, e.g. a delaying proxy): expected reason becomes
registry did not respond within 5s.No-regression check (#6857): with a normal registry and an older version installed, the update prompt still appears and auto-update still runs.
Unit tests and checks:
Key assertions to look at: startup emits
severity: 'warning'with the per-reason message (both the resolved-error path and the thrown path);severity: 'warning'rendersMessageType.WARNINGwhile absent/explicit-error severity staysMessageType.ERROR(install-failure regression guard); classifier tests cover each network code viaerror.codeand via message text (the global-npm path only surfaces codes in stderr text); a guard test pinsFETCH_TIMEOUT_MS ≥ 5000.Evidence (Before & After)
Before (unreachable registry, red
✕, generic message):After (same conditions, yellow
△, concrete reason + retry hint):Live tmux before/after for both the startup TUI and
qwen updatewas captured by the triage bot's independent verification run atb202c0b(see review comments above). The exact user-facing strings are also asserted verbatim in the updated tests.Negative control (tests genuinely pin the fix): with the test files kept and the production files reverted to
origin/main, the six suites fail 17 tests — restoring the fix brings them back to 134/134:origin/mainproduction code + new testsBlast radius (every other test file importing the touched modules):
gemini.test.tsx✅,standalone-update.test.ts✅,installationInfo.test.ts✅;AppContainer.test.tsxruns 114/121 with 7 failures in the unrelated terminal-title feature that fail identically on unpatchedorigin/mainin this environment (TTY-dependent, pre-existing — not introduced by this PR).Tested on
Environment (optional)
Linux x86_64, Node.js v22, npm workspaces install; verification via the vitest suites,
npm run check-i18n, and eslint on changed files.Risk & Scope
severitydefaults to'error', only the startup check branch opts into'warning', and regression tests assert install failures still render as errors. Secondary tradeoff: on broken networks the (now softer) notice can appear up to 3s later than before; the check runs as a post-render deferred task, so startup latency is unaffected.skippedstatus (development modeetc.) keeps its current styling — changing it is out of scope for Update check: soften timeout UX — warning instead of error, raise timeout budget #7049; no config/setting to customize the timeout (can be a follow-up if maintainers want it).Linked Issues
Commits:
b202c0b(the fix),a233b5f(review follow-up — translations for the six locales flagged in the triage review, so all nine locales now carry the new keys).中文说明
本 PR 做了什么
软化启动时更新检查失败的呈现方式,并给检查一个更符合现实的时间预算。在 npm registry 缓慢或不可达的网络环境下,Qwen Code 过去每次启动都会显示红色的
✕ Failed to check for updates. Please check your network or registry configuration.——对一个非阻塞的后台便利功能来说,这种措辞过于惊吓。本 PR 之后,同样场景会渲染为平和的黄色警告,说明实际发生了什么以及如何重试:△ Update check skipped (registry unreachable) — run /update to retry.三个配套改动:
npm outdated稳定耗时约 3 秒)。5 秒与同类 CLI 一致——Claude Code 的 autoUpdater 使用AbortSignal.timeout(5000)。该常量同时作用于 update-notifier 路径和 global-npm 的npm view子进程路径。update-failed事件负载新增可选字段severity?: 'error' | 'warning',默认'error',所有现有发射点不受影响。只有启动后台检查发出severity: 'warning',UI 渲染为黄色△警告。真正的更新安装失败保持红色✕错误;/update作为用户显式操作,同样保持错误样式。ENOTFOUND/ECONNREFUSED/EAI_AGAIN/ETIMEDOUT/ENETUNREACH,同时匹配error.code和 npm 子进程 stderr 文本)和registry 错误(其他情况)。四个调用点——启动检查、/update斜杠命令、qwen updateCLI、沙箱 update-relaunch 路径——的消息现在都包含具体原因。i18n:新字符串已翻译到全部九个语言(en、zh、zh-TW、ca、de、fr、ja、pt、ru),沿用各语言现有的 registry/更新术语;已无用的旧通用 key 从所有语言中移除。
npm run check-i18n通过。为什么需要
慢速或被屏蔽 registry 环境的用户(部分地区、公司代理)每次启动都会看到红色错误,尽管应用完全正常——issue #7049,下游真实用户报告见 #7044(中国用户,v0.19.11,"升级就报错")。失败显式化本身是正确且新近的修复(#6857 修复了真实的假阴性 bug,经 #6887 发布);问题在于呈现方式:对一个用户未主动请求、也不阻塞任何操作的后台检查使用了错误级红色样式和笼统文案。
本 PR 明确不是 #6857 / #6887 的回退。 失败保持可见("loud failure");只改变呈现方式、措辞和时间预算。
评审验证方案
如何验证
不可达 registry → 离线路径(启动 TUI):
npm_config_registry=http://192.0.2.1:1 qwen,预期启动数秒后出现黄色△ Update check skipped (registry unreachable) — run /update to retry.,启动不被阻塞,应用完全可用。不可达 registry → 显式
/update保持醒目错误并带原因:npm_config_registry=http://192.0.2.1:1 qwen update,预期 stderr 输出Failed to check for updates (registry unreachable). Please check your network or registry configuration.,退出码 1。超时路径(可达但缓慢的 registry,如延迟代理):预期原因变为
registry did not respond within 5s。无回归检查(#6857):正常 registry 下安装旧版本,更新提示仍然出现,自动更新仍然执行。
单元测试与检查:
packages/cli下运行六个受影响测试文件共 134 个用例全部通过;npm run check-i18n通过。重点断言:启动路径发出
severity: 'warning'及按原因区分的消息(resolved-error 与 thrown 两条路径);severity: 'warning'渲染MessageType.WARNING,缺省/显式 error 保持MessageType.ERROR(安装失败回归保护);分类器测试覆盖每个网络错误码的error.code与消息文本两种匹配方式;守卫测试锁定FETCH_TIMEOUT_MS ≥ 5000。证据(前后对比)
改动前(不可达 registry,红色
✕,笼统消息)与改动后(同等条件,黄色△,具体原因 + 重试提示)见上方英文部分。triage bot 在b202c0b的独立验证运行中捕获了启动 TUI 和qwen update的实机 tmux 前后对比(见上方评审评论)。用户可见字符串也在更新后的测试中逐字断言。测试平台
Linux ✅(本地:vitest 套件、check-i18n、changed-files eslint);macOS / Windows⚠️ (由 CI 矩阵覆盖)。
环境(可选)
Linux x86_64,Node.js v22,npm workspaces 安装。
风险与范围
severity默认'error',仅启动检查分支主动选择'warning',回归测试断言安装失败仍渲染为错误。次要权衡:坏网络下(现已软化的)提示最多比之前晚 3 秒出现;检查在渲染后延迟任务中运行,启动延迟不受影响。skipped状态(development mode 等)保持现有样式——修改它超出 Update check: soften timeout UX — warning instead of error, raise timeout budget #7049 范围;未提供超时配置项(若维护者需要可作为后续跟进)。关联 Issue
提交:
b202c0b(修复本体)、a233b5f(评审跟进——补齐 triage 评审指出的六个语言翻译,九个语言现已全部携带新 key)。补充证据(负向控制):保留测试文件、将生产代码还原到
origin/main时,六个套件失败 17 个用例(severity 缺失、旧文案、2 秒常量);恢复修复后回到 134/134 全绿——证明测试确实钉住了修复。波及面:导入被改模块的其余测试文件(gemini.test.tsx、standalone-update.test.ts、installationInfo.test.ts)全部通过;AppContainer.test.tsx114/121 通过,7 个失败位于无关的终端标题功能,在未修复的origin/main上同样失败(依赖 TTY 的预存环境问题,非本 PR 引入)。🤖 Generated with Claude Code