Skip to content

fix(sdk): Surface daemon JSON-RPC error details - #10571

Merged
doudouOUC merged 3 commits into
QwenLM:mainfrom
doudouOUC:fix/10570-daemon-http-error-details
Aug 31, 2026
Merged

doudouOUC merged 3 commits into
QwenLM:mainfrom
doudouOUC:fix/10570-daemon-http-error-details

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

What this PR does

When a daemon HTTP 5xx response uses the opaque JSON-RPC message Internal error, the TypeScript SDK now uses the first non-empty string available from data.details, data.message, or string data. The original HTTP status and parsed response body remain unchanged, and existing top-level error fallbacks are preserved.

Why it's needed

A failed Web Shell model switch can already carry the actionable provider reason in its HTTP response body, but the SDK previously discarded that reason while building DaemonHttpError.message. Users therefore saw only Set model failed: POST /session/:id/model: Internal error, which made configuration and provider failures indistinguishable from a daemon defect. The extraction is deliberately limited to 5xx responses with the exact generic top-level error and a numeric JSON-RPC code so specific errors, client errors, and non-JSON-RPC responses retain their existing display behavior.

Issue #10564 is related but covers the separate failed-turn event path and nested data.error.message; this PR fixes the daemon HTTP client path described by #10570.

Reviewer Test Plan

How to verify

Send setSessionModel a mocked HTTP 500 response shaped as { error: "Internal error", code: -32603, data: { details: "Missing credentials" } } and confirm the resulting DaemonHttpError.message ends in Missing credentials while its status remains 500 and its body remains unchanged. Repeat with data.message and string data. Then confirm that an empty or non-string detail, a specific top-level error, a non-numeric code, and an HTTP 400 response all retain the previous top-level message.

Automated verification completed with the full DaemonClient.test.ts suite (385 passed), focused setSessionModel tests on the latest main (11 passed), SDK type checking, SDK build, and git diff --check.

Evidence (Before & After)

Before: POST /session/:id/model: Internal error

After: POST /session/:id/model: Missing credentials

These messages were captured by a deterministic in-memory SDK reproduction using the exact origin/main source and the same mocked daemon response. A live provider configuration was not mutated for reproduction.

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

Node.js 22.22.3 on macOS; Qwen Code 0.22.3 Web Shell baseline; SDK tests use a mocked fetch response.

Risk & Scope

  • Main risk or tradeoff: The shared SDK HTTP error formatter now exposes string diagnostic data for every matching daemon 5xx JSON-RPC response, not only model switching. The exact top-level error, HTTP status, numeric-code, and string-value gates limit that expansion, and boundary tests cover each gate.
  • Not validated / out of scope: A live Web Shell provider-failure run was not performed because it would require mutating provider credentials. Duplicate HTTP/SSE notices, the separate [object Object] toast, nested data.error.message turn failures from bug(serve): Web Shell shows generic "Internal error" for failed turns, hiding the provider's actual error message #10564, and automatic retries are out of scope. The repository root build remains blocked in this worktree by the pre-existing local Ink type mismatch (selectable and selection frame exports); the affected SDK package build and type check pass.
  • Breaking changes / migration notes: None.

Linked Issues

Fixes #10570

Related: #10564 covers a different failed-turn event path and is not fixed by this PR.

中文说明

本 PR 做了什么

当 daemon HTTP 5xx 响应使用不透明的 JSON-RPC 消息 Internal error 时,TypeScript SDK 现在会依次使用 data.details、data.message 或字符串形式 data 中第一个非空字符串。原始 HTTP 状态码和解析后的响应体保持不变,现有顶层错误回退行为也予以保留。

为什么需要这个改动

Web Shell 模型切换失败时,HTTP 响应体中可能已经带有可操作的 provider 原因,但 SDK 之前在构造 DaemonHttpError.message 时丢弃了该原因。用户因此只能看到 Set model failed: POST /session/:id/model: Internal error,无法区分配置或 provider 故障与 daemon 自身缺陷。提取逻辑被有意限制为:HTTP 5xx、顶层错误严格等于通用 Internal error、且 JSON-RPC code 为数值;因此具体错误、客户端错误和非 JSON-RPC 响应仍保持原有展示行为。

Issue #10564 与此相关,但它处理的是另一条失败 turn 事件链路以及嵌套的 data.error.message;本 PR 修复的是 #10570 描述的 daemon HTTP 客户端链路。

Reviewer 测试计划

如何验证

让 setSessionModel 接收一个形如 { error: "Internal error", code: -32603, data: { details: "Missing credentials" } } 的模拟 HTTP 500 响应,确认生成的 DaemonHttpError.message 以 Missing credentials 结尾,同时状态码仍为 500、响应体保持不变。再分别验证 data.message 和字符串形式 data。随后确认空或非字符串详情、具体顶层错误、非数值 code 和 HTTP 400 响应都继续使用原有顶层消息。

自动化验证已完成:完整 DaemonClient.test.ts 测试套件通过 385 项;在最新 main 上聚焦的 setSessionModel 测试通过 11 项;SDK 类型检查、SDK 构建和 git diff --check 均通过。

证据(改动前后)

改动前:POST /session/:id/model: Internal error

改动后:POST /session/:id/model: Missing credentials

这些消息来自确定性的内存 SDK 复现:分别加载精确的 origin/main 源码和当前改动,并使用相同的模拟 daemon 响应。复现过程中没有修改真实 provider 配置。

测试平台

操作系统 状态
🍏 macOS ✅ 已测试
🪟 Windows ⚠️ 未测试
🐧 Linux ⚠️ 未测试

环境(可选)

macOS 上的 Node.js 22.22.3;Qwen Code 0.22.3 Web Shell 基线;SDK 测试使用模拟 fetch 响应。

风险与范围

  • 主要风险或取舍:共享的 SDK HTTP 错误格式化逻辑现在会为所有匹配的 daemon 5xx JSON-RPC 响应展示字符串诊断数据,而不只影响模型切换。顶层错误精确匹配、HTTP 状态、数值 code 和字符串值四道门槛限制了影响范围,边界测试覆盖了每一道门槛。
  • 未验证 / 超出范围:没有执行真实 Web Shell provider 故障,因为这需要修改 provider 凭据。重复的 HTTP/SSE 通知、单独的 [object Object] toast、bug(serve): Web Shell shows generic "Internal error" for failed turns, hiding the provider's actual error message #10564 中嵌套 data.error.message 的 turn 失败以及自动重试均不在本 PR 范围内。该 worktree 的仓库根构建仍被既有的本地 Ink 类型不匹配阻断(缺失 selectable 和 selection frame exports);受影响 SDK 包的构建和类型检查均通过。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

修复 #10570

相关:#10564 处理另一条失败 turn 事件链路,本 PR 不修复该问题。

@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Aug 30, 2026
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review follow-up for 3241aae9bd:

Item Action Rationale
R1-1 Not taken The reproduced model-switch path uses failOnError; changing latent XHR/SSE/ACP stream paths would expand the observed issue and declared PR scope.
R1-2 Fixed Added four cases pinning details precedence, fallback from empty details, empty string data, and empty message. All three requested mutants are killed.
R1-3 Not taken The daemon forwarding behavior predates this SDK-only fix; adding a CLI package contract test would create an unrelated cross-package expansion.

Verification: full DaemonClient.test.ts suite passed (389/389), SDK typecheck passed, SDK build passed, git diff --check passed, and independent isolated mutation checks killed all three requested mutants.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review round 2 summary (394c4d7)

Item Decision Action
R2-1 Declined Kept the numeric JSON-RPC code gate; code-less ACP transport parity requires a separate redaction and producer-contract decision outside #10570.
R2-2 Fixed Added coverage for a non--32603 numeric code and null data, with both requested mutation checks.

Verification: Prettier check, SDK typecheck, SDK build, and DaemonClient.test.ts (391/391) passed.

@doudouOUC doudouOUC self-assigned this Aug 31, 2026
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@doudouOUC
doudouOUC enabled auto-merge August 31, 2026 02:30

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tier: Standard — 2 files, SDK bug fix (+196/−3).

CI — 30 checks; 29 completed / skipped, 1 route: success. No build or integration-test job ran on the HEAD SHA. The PR description states 391 DaemonClient tests passed locally plus SDK typecheck and build; those results are not machine-verified by CI on this commit.

What I checked:

Logic correctness in failOnError:

  • Array safety: !Array.isArray(body) and !Array.isArray(data) are both guarded — neither a body array nor a data array is mistaken for a plain object.
  • Null safety: data && typeof data === 'object' correctly rejects null (null is falsy). Confirmed by the "null data" test case.
  • Gate correctness: res.status >= 500, errorBody?.['error'] === 'Internal error', and typeof errorBody['code'] === 'number' together limit the unwrap to opaque generic 5xx JSON-RPC responses. Client errors (400), specific top-level error strings, and non-numeric codes fall through to the existing display behavior.
  • Precedence: string data → data.details → data.message → top-level error, each gated by .length > 0.
  • data: undefined: unreachable by the guards (fails typeof data === 'string' and data && typeof data === 'object'); detail stays at top-level error. No test needed.

Tests: The 14-row parametric table covers all declared boundary cases. One observation:

The test named 'non-internal numeric code' uses code: -32000 and expects expected: 'Server is draining' — i.e., it verifies that a non--32603 numeric code does trigger unwrapping. The name says "non-internal" but the outcome is "yes, unwraps". The test body is correct; the name is inverted relative to the expectation and will mislead future readers. (Suggestion — not a blocker.)

Cross-check vs. existing reviews:

  • R1-1 (extend to XHR upload path): author declined — out of scope of the reproduced model-switch path. Accepted.
  • R1-2 (precedence + empty-string guard tests): fixed ✓ in earlier commit.
  • R1-3 (daemon-side contract pin): author declined — cross-package scope. Accepted.
  • R2-1 (ACP transport path): author declined — intentional scope boundary. Accepted.
  • R2-2 (null data + non-internal numeric code boundary rows): fixed ✓ in HEAD commit (394c4d7).

No blockers. All suggestions addressed or intentionally declined with clear justification.

Approving.

@doudouOUC
doudouOUC added this pull request to the merge queue Aug 31, 2026
Merged via the queue into QwenLM:main with commit f6cca89 Aug 31, 2026
163 of 167 checks passed
@doudouOUC
doudouOUC deleted the fix/10570-daemon-http-error-details branch August 31, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(sdk): Daemon HTTP errors drop JSON-RPC details

2 participants