Repository navigation
[1/3] Error cause in core and network - #289
Conversation
|
更正:描述里"消息文字不变"不准确。 URL 剥离只覆盖 本 PR 新增了 |
|
上面三点已在 bebb111 处理:CHANGELOG Breaking 写明传输错误消息从 Generated by Claude Code |
…entation Line numbers now refer to master 4220847 (#268 merged). The text follows what #289-#293 implement: TypeValidation is the cause of schema failures, transport messages drop the URL, and section 12 names the three PRs. The open questions in section 10 are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
…s in #276 The findings from implementing #289-#293 (TypeValidation, transport messages without the URL, the call-site count, the download error type) move to issue #276. Only the baseline and line numbers stay updated to master 4220847. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
The RFC is still open, so what #289-#293 found belongs in its text: TypeValidation as the cause of schema failures, transport and download messages without the URL, the 117 call sites, and reqwest::Error as the download error. Baseline and line numbers stay at master 4220847. Section 10's open questions are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
Describes how aimux's error system differs from the AI SDK's, traces today's error paths from the public entry functions to the lines that drop the underlying error, and designs `cause` across the core, the transport layer, the ops envelope, the generated DTOs and every binding. The open questions in section 10 stay open. Line numbers refer to master 4220847, and the text includes what implementing #289, #290 and #293 found. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
a04f514 to
7d78192
Compare
eric8810
left a comment
There was a problem hiding this comment.
Review
未发现阻塞项。ErrorCause/SourceError、ApiCallError.cause、URL 去除、重试语义和 DTO 生成都成立,真实连接测试也覆盖了 source 链和 query 不泄漏。
建议后续修的点(不阻塞):
aimux-provider-utils/src/fetch.rs的连接失败测试先 bind 再 drop 端口,存在端口被复用和系统代理干扰的偶发失败风险;建议改成确定性 mock,或保持 listener 绑定。contract-tests/dto只有SourceError形状的 cause 向量,缺少嵌套AiMuxcause 的跨语言向量。docs/rfc-0039-conformance.md的schema/types文件数仍是 297,新增后应为 300;CHANGELOG 漏了invalid_response_api_call的签名变化。- Bedrock event stream、Stability/xAI 的 JSON 解析 cause 在 #290 才补;如果 #289 单独合并,第一步的覆盖不完整。
- 分支基于
42208472,合并前建议 rebase 到当前 master。
ErrorCause is a nested AiMuxError or a SourceError chain copied from an external error's source() chain. It is what source() returns and serializes as an optional `cause`. ApiCallError gains cause and caused_by(..); RetryError's source is its last attempt. Part 1 of RFC-0047. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
FetchError carries a SourceError built from reqwest's error after without_url(), and fetch, redirects, body reads, the JSON and SSE handlers and WebSocket keep the original error as the cause. Transport messages lose the URL: 'error sending request for url (...)' is now 'error sending request'. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
A Swift struct cannot store itself inline, so SourceError.cause did not compile. The field keeps its public name and type and is stored in a WireIndirect. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
ErrorCause and SourceError reach every binding through the generator. AiMuxError/004 and AiMuxErrorInput/004 carry an ApiCall cause chain and are registered in index.json and sources.json. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
CHANGELOG Breaking, the request pipeline's cause row, and 228 DTO vectors in the conformance doc. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
Describes how aimux's error system differs from the AI SDK's, traces today's error paths from the public entry functions to the lines that drop the underlying error, and designs `cause` across the core, the transport layer, the ops envelope, the generated DTOs and every binding. The open questions in section 10 stay open. Line numbers refer to master 4220847, and the text includes what implementing #289, #290 and #293 found. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
7d78192 to
bec5828
Compare
|
回复 @eric8810 的 review(左列是 eric 的原话):
本地:fmt、 Generated by Claude Code |
* docs(rfc): RFC-0047 error system and error cause Describes how aimux's error system differs from the AI SDK's, traces today's error paths from the public entry functions to the lines that drop the underlying error, and designs `cause` across the core, the transport layer, the ops envelope, the generated DTOs and every binding. The open questions in section 10 stay open. Line numbers refer to master 4220847, and the text includes what implementing #289, #290 and #293 found. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C * docs(rfc): answer the RFC-0047 review Retry's lastError as every binding's native cause, the Node and Java cause classes, an accurate security section with a size and redaction question, the full list of breaking changes, Swift's WireIndirect, and how RFC-0044, RFC-0048 and #349 meet this RFC. Q2 and Q3 are marked adopted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in the error-cause series (#289, #290, #293). Conflicts: ops tests take master's `InvalidParams { .. }` without `unprojected()`; envelope.rs keeps `BindingError::result_serialization` without a clock; binding READMEs and Node error.ts keep the cause text without retryAfterMs. Bindings regenerated with aimux-codegen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G5j9hYv7hDeYgMvvRu2F9T
…h-the-ai-sdk master's error-cause change (#289/#290/#293) made InvalidResponseData a struct variant; closed_before_finishing now builds it with AiMuxError::invalid_response_data. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Requested by li · project thread
Summary
RFC-0047(#286)实现的第 1 步:错误原因的核心类型,以及网络层把底层错误作为
ApiCallError.cause保留下来。网络连不上时,调用方能沿source()一直看到Connection refused (os error 111),而不是只有 “error sending request”。传输层(fetch.rs和 Vertex 的证书传输)先去掉 URL(query 可能带 API key),所以原因链和消息里都没有 URL:传输错误的消息从error sending request for url (…)变成error sending request,其他消息不变。下载错误(aimux-core/src/download.rs,用自己的 reqwest 客户端)在 #290 处理。基于 master
e115e723(#268 已合并),用 #268 的生成器生成 DTO;下面概览的行号是开 PR 时的 master42208472。后面还有两步:#290 六个字符串变体带 cause,#293 ops 信封和各语言绑定。网络层错误体系概览
一次请求从 provider 到传输层再回来,错误在这几层产生,行号是 master
42208472(#268 合并后):这些位置都把底层错误
to_string()进消息,错误本身丢掉:FetchError的载荷是String,reqwest 的原因链只剩第一层文字,而且文字里带着完整 URL。AI SDK 在同样的位置都把原错误作为APICallError.cause。分类只看结构化字段(status_code、is_retryable、response_headers),本 PR 不改分类。本 PR 的改动
核心类型,
aimux-core/src/error.rs:网络层,
aimux-provider-utils:provider 里自带
Fetch的三处同样改为SourceError:Azure 的 Bearer token、openai-compatible 的 User-Agent、Vertex 的证书传输(它也加了without_url())。Jev 和 decision 支持代码按新签名把错误本身传给invalid_response_api_call。Bedrock 的 event stream、Stability 和 xAI 的 JSON 解析在本 PR 就把原错误作为ApiCallError.cause,这一步单独合并时网络层也是完整的。调用方看到的(
fetch.rs新测试里连一个不监听的端口,实际打印的source()链):DTO:
ErrorCause、SourceError由 #268 的生成器从 Rust 类型生成到每种语言,没有手写。Swift 的结构体不能直接存自己类型的字段,生成器对这种字段(SourceError.cause)改为存在WireIndirect里,对外仍是public var cause: SourceError?(tools/aimux-codegen/src/emit/swift.rs,带测试)。contract-tests/dto/AiMuxError/004.json和AiMuxErrorInput/004.json是带传输链的ApiCall向量(输出和输入两个方向),取自 ops 错误黄金样例aimux-api-call.json;005.json是 cause 为嵌套AI_JSONParseError的ApiCall,取自post_to_api.rs的新测试。都登记在index.json和sources.json,向量总数从 226 变成 230。各语言把 cause 还原成原生的 cause 在第 3 步。文档:CHANGELOG Breaking(含传输错误消息去掉 URL、
invalid_response_api_call的签名变化),docs/ai-sdk-request-pipeline.md里 “本轮不加cause” 改为现在的做法和差异(外部错误存成可序列化的链、消息仍含原文字、URL 去掉),docs/rfc-0039-conformance.md的向量数改为 230、类型文件数改为 300。Type of change
Breaking:
FetchError的载荷从String改为SourceError;invalid_response_api_call第一个参数改为错误本身;传输错误的消息不再带 URL。JSON 只多一个可选字段。Checklist
cargo fmt --all -- --checkpassescargo clippy --workspace --all-targets -- -D warningspassescargo test --workspacepasses (cassette playback — no network/keys)README.md,docs/,rfc/,bindings/README.md)新测试:
fetch.rs真的连一个绑定着但不监听的端口(测试期间一直持有,端口不会被别人复用;显式代理配置加no_proxy,环境代理不影响),检查原因链到 OS 错误、链里没有 URL 里的 key、ApiCallError.cause就是这条链;post_to_api.rs检查 2xx handler 失败时 cause 是整个嵌套的 aimux 错误;error.rs检查 cause 的source()链、JSON 往返、嵌套 aimux 错误保留名字、Retry的 source 是最后一次尝试。Notes for reviewers
ErrorCause的有序无标签联合(生成器已有Kind::Untagged),以及without_url()是否覆盖了所有 reqwest 错误出口。