Skip to content

[2/3] Error cause in string variants - #290

Merged
eric8810 merged 15 commits into
masterfrom
cause/2-error-cause-in-string-variants
Oct 21, 2026
Merged

eric8810 merged 15 commits into
masterfrom
cause/2-error-cause-in-string-variants

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Requested by li · project thread

Summary

RFC-0047(#286)实现的第 2 步:只带一个字符串的六个错误变体改成 { message, cause },117 处构造它们的地方把手上的底层错误(serde、base64、url、io、TLS)作为 cause 保留;InvalidToolInput.cause 从字符串改为错误对象。值不符合 schema 时的 cause 是新增的 TypeValidation(AI_TypeValidationError),和上游一样。下载错误先去掉 URL,所以下载失败的消息不再带 URL;其他消息文字不变。

叠在 #289(第 1 步:核心类型和网络层)上。第 3 步是 ops 信封和各语言绑定(#293)。

字符串变体错误体系概览

基于 #289,整条栈在 master e115e723 上。六个变体在调用路径的各层产生,行号是开 PR 时的 master 42208472(#268 合并后):

generate_text / stream_text                     aimux-core/src/generate.rs
├ prompt 转换 → InvalidPrompt(String)            aimux-core/src/language_model_message.rs:313 / 380 等 7 处
├ provider do_generate / do_stream
│ ├ 选项解析 → InvalidArgument(String)           各 provider 的 parse_options(azure/transcription.rs:70 等)
│ ├ 请求体:base64、URL、文件、凭证 → InvalidArgument(String)   aimux-providers 141 个文件
│ └ 响应体:serde → From<serde_json::Error>      aimux-core/src/error.rs:636
│     ├ Syntax / Eof / Io → JsonParse(String)
│     └ Data → InvalidResponseData(String)
├ 工具调用:parse_tool_call                      aimux-core/src/parse_tool_call.rs:160
│ ├ JSON 解析失败 → InvalidToolInput { cause: "JSON parsing failed: …" }        :305
│ └ schema 校验失败 → InvalidToolInput { cause: "Type validation failed: …" }   :291
├ 结构化输出:parse_complete_output             aimux-core/src/output.rs:188
│ ├ 不是 JSON → JsonParse(String)                output.rs:253
│ └ 形状不对 → InvalidResponseData(String)       output.rs:190
├ 下载:download_inner                           aimux-core/src/download.rs:240   reqwest 错误 → Other("download failed: {error}"),文字带 URL
└ 没有输出 → NoOutputGenerated(String)           generate.rs:500 / 904 / 1915
ops、aimux-web、mock 回放 → Other(String) / InvalidArgument(String)

这些位置都是 format!("…: {error}"):底层错误的文字进了消息,错误本身丢掉。AI SDK 的 JSONParseError、TypeValidationError、InvalidArgumentError、InvalidPromptError、NoOutputGeneratedError、AISDKError 构造时都接受 cause,InvalidToolInputError.cause 是解析或校验错误对象。

本 PR 的改动

类型,aimux-core/src/error.rs:

AiMuxError
├ JsonParse / InvalidResponseData / InvalidArgument / InvalidPrompt / NoOutputGenerated / Other
│   (String) → { message, #[source] cause: Option<ErrorCause> }
│   + 构造函数 json_parse(..) / invalid_response_data(..) / invalid_argument(..) / invalid_prompt(..) / no_output_generated(..) / other(..)
│   JSON 只多一个可选 "cause";Display 和错误码不变
├ InvalidToolInput { cause: String → #[source] ErrorCause }
└ + TypeValidation { message, value, cause }    AI SDK TypeValidationError(@ai-sdk/provider errors/type-validation-error.ts)
    只作为 cause 出现;ops 错误码和 InvalidResponseData 共用 3
From<serde_json::Error>                         serde 错误本身作为 cause

构造处,按底层错误分:

有外部错误(serde、base64、url、io、reqwest、ring)
└ AiMuxError::InvalidArgument { message: format!("…: {e}"), cause: Some(ErrorCause::source(&e)) }   117 处
   消息照旧拼原文字,C ABI 只看 message 的调用方不少信息
没有底层错误(就地检查失败)
└ AiMuxError::invalid_argument("…")              448 处,只是换成构造函数
aimux-web                                       tools/aimux-web/src/api/replay.rs、traces.rs   serde 错误作为 cause

工具调用和结构化输出,和 AI SDK 一样拆成两层:

parse_tool_call                                 aimux-core/src/parse_tool_call.rs
├ JSON 解析失败                                 上游 parse-json.ts:106-109 safeParseJSON
│ └ InvalidToolInput { cause: JsonParse { message: "JSON parsing failed: Text: …", cause: 解析器错误 } }
└ schema 校验失败                               上游 validate-types.ts:79 safeValidateTypes
  └ InvalidToolInput { cause: TypeValidation { message: "Type validation failed: Value: …", value, cause: jsonschema 错误 } }
parse_complete_output                           aimux-core/src/output.rs
├ 不是 JSON → JsonParse { cause: JsonParse }
├ 形状不对 → InvalidResponseData { cause: TypeValidation }   上游 NoObjectGeneratedError.cause
└ 调用方给的 schema 本身非法 → InvalidResponseData { cause: SourceError }   和 parse_tool_call 一样,不说成值不符合
json_parse_error / type_validation_error / validator_error   两处共用,消息模板不变

调用方看到的(Display 取 cause 的 message,文字和以前的字符串一样;JSON 见 aimux-core/tests/error_value_golden_test.rs):

Invalid input for tool weather: JSON parsing failed: Text: {.     ← Display,不变
└ cause: JSON parse error: JSON parsing failed: Text: {.          ← source(),JSON 里是 {"name":"AI_JSONParseError", …}
  └ cause: 解析器自己的错误                                        ← 只在 Rust 和 JSON 的 cause 里

下载,aimux-core/src/download.rs:

download_inner
├ 构造客户端、send、读 body 失败 → transport_error(reqwest::Error)
│   └ download_error(error.without_url())       消息和 cause 都用去掉 URL 的错误(签名 URL 的 query 带凭证)
└ Location 头、URL 拼接失败 → download_error(e) 这些错误的文字里没有 URL

DTO:类型变化由 #268 的生成器重新生成到每种语言;contract-tests/dto/AiMuxError/006.json(InvalidToolInput 的 cause 是 AI_JSONParseError)、007.json(InvalidResponseData 带嵌套 cause)取自 ops 错误黄金样例,这两个样例也一起更新;008.json 是带 value 和校验器 cause 的 AI_TypeValidationError(取自新黄金样例 aimux-type-validation.json),009.json 是 value: null、没有 cause 的 AI_TypeValidationError。AiMuxErrorInput 有同样四个,都登记在 contract-tests/dto/index.json 和 sources.json,向量总数从 230 变成 238。新增 ops 错误黄金样例 aimux-type-validation.json;Python、Java、Kotlin 的异常类按名字分派,各加一个 TypeValidationError。

文档:CHANGELOG Breaking 合并成一条,docs/error-model.md 新增「错误原因」一节(含 TypeValidation),docs/ai-sdk-request-pipeline.md 的 cause 一行改为所有接受 cause 的错误类,docs/rfc-0039-implementation.md 的错误码 3 别名加上 TypeValidation,docs/rfc-0039-conformance.md 的向量数改为 238。

新测试:parse_tool_call.rs 经过 parse_tool_call 测两种失败,非法 JSON 的 cause 是带解析器错误的 AI_JSONParseError,不符合 schema 的 cause 是带 value 和校验器错误的 AI_TypeValidationError;output.rs 检查结构化输出不符合 schema 时 cause 是 TypeValidation,schema 本身非法({"type": 12})时 cause 是 SourceError;download.rs 用带 ?X-Amz-Signature=SECRET 的 URL 检查消息和 cause 都不含 query。

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • New provider
  • New / updated binding
  • Docs / RFC

Breaking:六个变体从元组变体改为结构体变体,InvalidToolInput.cause 类型变化,新增变体 TypeValidation(对 AiMuxError 做穷尽匹配的代码要加一支)。已有错误名、错误码不变;JSON 里 InvalidToolInput.cause 从字符串变为错误对象;下载失败的消息不再带 URL。

Checklist

  • cargo fmt --all -- --check passes
  • cargo clippy --workspace --all-targets -- -D warnings passes
  • cargo test --workspace passes (cassette playback — no network/keys)
  • Added or updated cassette tests / contract fixtures for behavior changes
  • Updated relevant docs (README.md, docs/, rfc/, bindings/README.md)
  • Commit messages follow Conventional Commits

Notes for reviewers

  • 设计见 RFC-0047(RFC-0047: 错误体系与错误原因(cause) #286)§4.2。
  • 改动大部分是机械的(构造函数替换、模式 X(_) → X { .. });值得看的是 error.rs、output.rs、parse_tool_call.rs,以及 provider 里 117 处带 cause 的地方是否选对了底层错误。
  • jsonschema 的错误借用被校验的值,不能存成 'static 的 source,所以它只以文字形式进 SourceError,挂在 TypeValidation.cause 下。
  • TypeValidation 只加了上游的 value,没有加 context(field、entityName、entityId):aimux 现在没有地方填它。

@cunninghamcard-bit

Copy link
Copy Markdown
Contributor Author

本地实验(c9e8dd42):把 aimux-core/src/parse_tool_call.rs 里两处 InvalidToolInput.cause 换成常量 SourceError::from("x"),cargo test -p aimux-core -p aimux-ops 仍然全部通过。只把内层传给 json_parse_error 和 type_validation_error 的 cause 改成 None,cargo test -p aimux-core 也全部通过。所以工具输入的 cause 内外两层现在都没有行为测试。我会补两个经过 parse_tool_call 的测试:一个输入非法 JSON,断言 cause 是 AI_JSONParseError 并带解析器错误;一个输入不符合 schema,断言 cause 的类型和 name。

上游在 schema 校验失败时,cause 是 TypeValidationError,name 为 AI_TypeValidationError(@ai-sdk/provider-utils@5.0.55 的 src/validate-types.ts:79 的 safeValidateTypes,JSON 解析失败时是 src/parse-json.ts:106-109 的 safeParseJSON;ai@7.0.127 的 packages/ai/src/generate-text/parse-tool-call.ts:259-262;@ai-sdk/provider@4.0.22 的 src/errors/type-validation-error.ts:4)。我们现在给的是没有 name 的 SourceError,这一点我也会对齐。

download_error(aimux-core/src/download.rs:317-322)现在把整个 reqwest::Error 放进 cause,没有先调 without_url(),message(:319 的 format!("download failed: {error}"))用的也是同一个没去 URL 的错误,所以下载 URL 连同 query 会同时进 message 和 cause 链,再经 #293 到各语言的 error.message 和 error.cause。本地用一个带 ?X-Amz-Signature=SECRET 的 URL 试过,序列化出的 cause 里有 SECRET;reqwest::Error 的 Display 带 URL,所以 message 里同样会有。我会在 download_error 入口先调 without_url(),message 和 cause 都用去过 URL 的错误,并补一个测试,同时断言 message 和 cause 都不含 query。

docs/rfc-0039-conformance.md 的 :261 和 :305 两处向量数我会改成 232。#293 里把 :261 改成 232 的那处改动随之去掉,避免 #289、#290、#293 三个 PR 改同一行。

Copy link
Copy Markdown
Contributor Author

已在 402bce5 处理:

意见 改动
工具输入的 cause 内外两层没有行为测试 parse_tool_call.rs 新增两个经过 parse_tool_call 的测试:非法 JSON 断言 cause 是带解析器错误的 AI_JSONParseError;不符合 schema 断言 cause 是 AI_TypeValidationError,检查 value、消息模板和校验器错误
schema 校验失败的 cause 应是 AI_TypeValidationError 新增 AiMuxError::TypeValidation { message, value, cause },工具输入和结构化输出(InvalidResponseData.cause)都用它;ops 错误码和 InvalidResponseData 共用 3;新增黄金样例 aimux-type-validation.json;Python、Java、Kotlin 加 TypeValidationError 类
download_error 把带 query 的 URL 放进 message 和 cause reqwest 错误经 transport_error 先 without_url(),message 和 cause 都用去掉 URL 的错误;新测试用 ?X-Amz-Signature=SECRET 断言两者都不含 query 和主机
向量数 docs/rfc-0039-conformance.md 两处改为 232,#293 不再改这一行

Generated by Claude Code

@cunninghamcard-bit
cunninghamcard-bit marked this pull request as ready for review October 20, 2026 10:41
@cunninghamcard-bit
cunninghamcard-bit force-pushed the cause/1-error-cause-in-core-and-network branch from 8606180 to a04f514 Compare October 20, 2026 10:48
cunninghamcard-bit added a commit that referenced this pull request Oct 20, 2026
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
@cunninghamcard-bit
cunninghamcard-bit force-pushed the cause/2-error-cause-in-string-variants branch 2 times, most recently from 095464c to e3a861b Compare October 20, 2026 10:51
@cunninghamcard-bit
cunninghamcard-bit force-pushed the cause/1-error-cause-in-core-and-network branch from a04f514 to 7d78192 Compare October 20, 2026 10:51

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review

核心改动(六个变体、构造函数、消息/错误码、下载 URL 脱敏)基本正确,但有一个安全问题需要先修:

  • 重要(安全) aimux-providers/src/bedrock/model.rs:54:bytes_stream() 的错误直接作为 cause,没有先 without_url()。reqwest 的 body/decode 错误可能带完整 URL 和 query,与 #289 的 URL 去除规则不一致,存在把签名 URL/密钥写进 cause 的风险。建议 let error = error.without_url(); 后 message 和 cause 都用它。
  • 次要 aimux-core/src/output.rs 对 jsonschema::validator_for(schema) 失败用了 TypeValidation,而 parse_tool_call.rs:286 同样场景用 SourceError;建议统一,避免把“调用方 schema 非法”说成“模型值不符合 schema”。
  • 次要 缺少 AI_TypeValidationError 的 DTO 向量(value + nullable cause);tools/aimux-web/src/api/replay.rs 和 traces.rs 仍丢弃 serde cause。
  • 提示 parse_tool_call.rs 新增测试插在了 integral-number 测试的文档注释和测试之间,注释位置需要调整。

cunninghamcard-bit and others added 12 commits October 20, 2026 14:26
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
JsonParse, NoOutputGenerated, InvalidResponseData, InvalidArgument,
InvalidPrompt and Other become { message, cause }, with constructors of
the same names; From<serde_json::Error> keeps the serde error as the
cause. InvalidToolInput.cause is an ErrorCause instead of a string.

The new TypeValidation { message, value, cause } is the AI SDK's
TypeValidationError. It only appears as a cause and shares ops error
code 3 with InvalidResponseData.

Part 2 of RFC-0047.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
As in the AI SDK (parse-json.ts, validate-types.ts, parse-tool-call.ts),
InvalidToolInput's cause is the JsonParse or TypeValidation error itself,
and structured output that does not match its schema has a
TypeValidation cause. Download transport errors drop the URL before
they become the message and the cause: a signed URL carries its
credentials in the query.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
The sites that formatted an underlying error into one of the six
variants now also keep it as the cause; the others use the new
constructors. Messages are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
…-web

The ops manifest lists TypeValidation under error code 3, and the ops
error goldens show InvalidToolInput and InvalidResponseData with their
causes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
The generated types follow the new variant shapes. Python, Java and
Kotlin dispatch errors by name and get a TypeValidationError class.
AiMuxError/005-006 and AiMuxErrorInput/005-006 come from the ops
error goldens.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
CHANGELOG Breaking, the error model's cause section, the code 3
aliases, 232 DTO vectors, and RFC-0035's repair-context example.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2GJxE3fH7rQvAwXLg2f1C
@cunninghamcard-bit
cunninghamcard-bit force-pushed the cause/2-error-cause-in-string-variants branch from e3a861b to b570ada Compare October 20, 2026 14:43
@cunninghamcard-bit
cunninghamcard-bit force-pushed the cause/1-error-cause-in-core-and-network branch from 7d78192 to bec5828 Compare October 20, 2026 14:43
cunninghamcard-bit added a commit that referenced this pull request Oct 20, 2026
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

Copy link
Copy Markdown
Contributor Author

回复 @eric8810 的 review(左列是 eric 的原话):

eric 的意见 处理 位置
「bedrock/model.rs:54:bytes_stream() 的错误直接作为 cause,没有先 without_url()」 不用改:bytes_stream() 产出的是 FetchError,不是 reqwest::Error,FetchError 上也没有 without_url()。URL 在传输层已经去掉:ReqwestFetch 的 body 流经 map_reqwest_error 先 without_url()(fetch.rs:451),Vertex 的证书传输经 fetch_error(vertex/auth_certificate.rs:198,同样 without_url()),aimux 自带的 Fetch 产出的 body 错误都不带 URL #289 b034155 aimux-provider-utils/src/fetch.rs map_reqwest_error
「output.rs 对 jsonschema::validator_for(schema) 失败用了 TypeValidation,而 parse_tool_call.rs:286 同样场景用 SourceError;建议统一」 统一为 SourceError:schema 非法是调用方的错,cause 不再说成值不符合 schema;schema_mismatch 改为接收 ErrorCause,新测试用 {"type": 12} 检查 b6e5710 aimux-core/src/output.rs validate,测试 an_invalid_schema_is_not_a_type_validation_cause
「缺少 AI_TypeValidationError 的 DTO 向量(value + nullable cause)」 新增 AiMuxError/008.json(带 value 和校验器 cause,取自 aimux-type-validation.json)、009.json(value: null、没有 cause),AiMuxErrorInput 同样两个;向量数 238 f719e4b contract-tests/dto,b570adac docs/rfc-0039-conformance.md
「tools/aimux-web/src/api/replay.rs 和 traces.rs 仍丢弃 serde cause」 两处的 serde 错误都作为 cause 保留 4ba0a11 tools/aimux-web/src/api/replay.rs、traces.rs
「parse_tool_call.rs 新增测试插在了 integral-number 测试的文档注释和测试之间」 注释移回 integral_input_numbers_are_written_as_javascript_writes_them 上方 b6e5710 aimux-core/src/parse_tool_call.rs

整条栈已 rebase 到 master e115e723;master 新增的 InvalidArgument 写法(#282)也跟着改成结构体变体。本地 fmt、clippy -D warnings、cargo test --workspace、生成器 --check、DTO 向量都通过。


Generated by Claude Code

@eric8810 eric8810 mentioned this pull request Oct 20, 2026
3 tasks done
eric8810 pushed a commit that referenced this pull request Oct 21, 2026
* 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>

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

复审通过。Bedrock body 错误经 FetchResponse.bytes_stream() 已经是 FetchError,传输层已 without_url();其余四点(schema 非法统一为 SourceError、AI_TypeValidationError 向量、Web serde cause、注释位置)都已落实。

合并前还需把栈 rebase 到当前 master 并 retarget 到 master:#289 已 squash 合入,当前 base 仍指向 cause/1-... 分支,直接合会带上 #289 的旧提交。

@eric8810
eric8810 changed the base branch from cause/1-error-cause-in-core-and-network to master October 21, 2026 03:04

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

评审:[2/3] Error cause in string variants —— 代码可批;分支上的合并提交必须重做(阻塞)

阻塞项(重要):当前分支头 20fc9c5f(由维护者侧评审流程误推,责任在我方)是一次丢弃 master 内容的合并——树哈希与合并前 tip 完全相同。照此合并会删除 rfc/0044、rfc/0046、rfc/0047(−1311 行,含本 PR 自己实施的规范)并回退 #333 的 CI 门禁改动(ci.yml)。GitHub 仍显示 MERGEABLE,但这是假象。请对 master 重做真实合并([1/3] 内容已验证与 master 等价,应无冲突)或至少恢复那 4 个文件后重推。修复后本 PR 可批。

代码评审(基于真实 [2/3] 增量,+3026/−1421):

  • RFC-0047 §2.5–2.7/§4.2/§9/§11 如实落地:六个字符串变体改形 {message, cause: Option<ErrorCause>}、TypeValidation 与 InvalidResponseData 共用码 3 并在 manifest 别名表登记、InvalidToolInput.cause 对象化(Display 旧文字不变)、serde 错误作为 cause、download.rs 的 transport_error() 消息与 cause 均不带 URL(有 ?X-Amz-Signature=SECRET 泄露测试)。
  • 构造点 448 处无 cause / 120 处带 cause 与自报吻合(47 个 provider 文件),无元组残留。
  • 生成物同步完整:八绑定 + schema/types + DTO 向量 238(新增 8 个,含输入方向 cause 的双向向量)+ manifest digest + ops golden + 错误模型文档——没有重蹈 #288 的生成链失步。
  • 测试覆盖两层失败模式(JsonParse vs TypeValidation vs SourceError)、wire 快照含嵌套 cause 形状、穷尽 match。

修复合并方式后即可合并。

@eric8810 eric8810 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

复核:阻塞已解除,改判批准

作者的 448460b3(restore master content lost by the cause stack merge)完整恢复了被误合并丢弃的内容:rfc/0044/0046/0047(+1311 行)与 ci.yml 的 #333 门禁均已核实在树中;分支与 master 的差异回到 [2/3] 真实增量(260 文件,+3034/−1429)。1a33d20e 的 refused-connection fixture 可移植化小修(fetch.rs 15 行)一并看过,无问题。

此前的阻塞项(维护者侧误推的丢弃式合并提交 20fc9c5f)就此中和——责任在我方评审流程,已在上一条评审中说明;作者以修复前进处理,等效且保留了栈上后续工作,无需撤销。

原评审对代码本体的结论不变(RFC-0047 §2.5–2.7/§4.2/§9/§11 逐条落地、生成物同步完整、构造点统计吻合、两层失败与 SECRET 泄露测试到位)。CI 绿后可合并;合并后 #293 记得 rebase。

@eric8810
eric8810 merged commit 2d89b1d into master Oct 21, 2026
22 checks passed
cunninghamcard-bit added a commit that referenced this pull request Oct 24, 2026
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
cunninghamcard-bit added a commit that referenced this pull request Oct 24, 2026
…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
cunninghamcard-bit added a commit that referenced this pull request Oct 24, 2026
Keeps this PR's API call error for a response without output (master's #290 had turned the old message into InvalidResponseData), and keeps both this PR's tests and master's raw-chunk tests (#285) at the end of open_responses_test.rs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants