fix/资源编辑错误通道补 typed 错误并把平台失败原文带出 #589

Merged
k88936 merged 18 commits from fix/res-edit-error-handling into master 2026-10-03 11:32:40 +08:00
Member

close #555

close #555
k88936 added 3 commits 2026-10-02 18:50:04 +08:00
- decision-log 新增 2026-10-02 条目:ResourceEditError 造型、不用 blanket From、不按 terminal_failure_code 分支、前缀去留、原文边界、Other 命名与 error.rs 落点
- 条目里写清 terminal_failure_code 的清理前置条件:字段无 skip_serializing_if 且账本是 deny_unknown_fields,清掉会影响每个已落盘账本
- pitfalls「远端资源编辑终态必须指出唯一出口」补上处理与验证口径:轮询终态把平台 error 原文装进 typed 错误带出、不再压成一句,断言同时要求原文进文案且不进账本
- 新增 project/resource_editor/error.rs:ResourceEditError 只有 RemoteGenerationFailed { server_message } 与 Other(String) 两个变体,to_user_msg 是唯一写「资源编辑生成失败:…」的地方,Other 上留 // TODO refactor string-typed
- 轮询到 status=failed 时读平台 error 原文装进 RemoteGenerationFailed 带出,账本仍只写 terminal_failure_code,原文不进账本
- 删掉轮询与提交期 HTTP 400 两处首句失败文案的 remote-terminal-failed 前缀;ensure_resource_edit_phase_resumable 里的三个 token 保留
- 两个入口拆成 typed 实现与 Result<_, String> 外观:derive_local_project_resource_typed / resume_local_project_resource_edit_typed,旧名映射 to_user_msg 供既有调用点与 Tauri 命令使用
- 不提供 impl From<String>,每处 String 错误显式 .map_err(ResourceEditError::Other)
- terminal_failure_code 加 // TODO clean unnecessary 并写明清理前置条件(字段无 skip_serializing_if、账本带 deny_unknown_fields)
- 测试:远端终态断言平台原文进文案且不进账本,抠图远端失败改用 typed 入口覆盖无 error 时的兜底
资源编辑远端失败原文透传到工具错误与诊断
Project CI / Backend tests (pull_request) Failing after 29s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m48s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 3m54s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 2m50s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m28s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m55s
Project CI / Native shell tests (pull_request) Successful in 6m48s
d2a5bceaba
- agent/tool/error.rs 新增共用载体 RemoteResourceEditFailure { serverMessage },两个工具的平台失败文案只写一份
- CreateOrDeriveResourceError 与 RemoveBackgroundError 各加 RemoteGenerationFailed(RemoteResourceEditFailure) 变体,远端终态失败不再压回字符串
- 两个工具各加显式 from_resource_edit_error 翻译,不用 impl From<ResourceEditError>;Other 仍落回各工具原有的「失败:<文案>」变体并留 TODO
- 桥里四个资源编辑调用点改用 typed 入口,并 Box::pin 后再 await:资源编辑 future 内联会顶穿 handle_direct_tool_bridge 状态机的调试测试线程栈
- assets.rs 新增 with_direct_editor_api_credentials_as,保留调用方 error 类型,凭据解析失败由调用方显式翻译成 ResourceEditError::Other,旧名与其余调用点零改动
- 工具错误模块补单测:平台原文进 message、进序列化的 typed 错误,无原文与未分类失败各自的文案
- decision-log 补工具层承载、凭据作用域错误类型、future 装箱三条决策与真实验证结果;pitfalls 补工具层原样透传口径
Author
Member
  • 1. account_api.rs:81-90 — error_code 与 error_message 重复解析(已修:675dfe0c8)

    • 现状实现(改前):两个 fn 各自 serde_json::from_str + get("error").unwrap_or(&value),只有取的字段名不同("message" / "code")。
    • 问题:同一份「解包 error 信封」逻辑维护两遍,加字段就得改两处,容易漏。
    • 已做:抽出 error_field(body, field),error_message / error_code 变成两个薄封装;调用点不动,行为不变。
  • 2. game_distribution_publish.rs:196-200 — message.or(code) 先于 .filter(已修:a0428da5d)

    • 现状实现(改前):message.or(code).filter(|d| !d.trim().is_empty())。Option::or 先求值,Some("") 会短路掉 code。
    • 问题:服务端给 {"error":{"code":"X","message":""}} 这种「有空 message + 有 code」时,先选中空 message,再被 filter 丢掉,最后落到「服务器未返回错误信息」,恰好丢掉本次改动想带出的 code。
    • 已做:改成 message.filter(非空).or_else(|| code.filter(非空)),每个候选先过滤再退下一个。
    • 附带:同文件的 map_http_error 是同一个写法(message.or(code).filter(...)),review 没列,但属同一类 bug,一并修了(同提交)。
  • 3. game_package_upload/runtime.rs:91-95 — read_upload_state 同款顺序 bug(已修:1a99a2f26)

    • 现状实现(改前):message.or(code).filter(...).unwrap_or_else(|| format!("HTTP {status}"))。
    • 问题:与 2 完全同型;服务端给空 message + 有效 code 时 code 被吞,只剩裸 HTTP {status}。
    • 已做:改成 message.filter(...).or_else(|| code.filter(...))。
  • 4. game_package_upload/runtime.rs:146-151 — 三步共用裸 HTTP {status},分不出是哪一步(已修:cdccfa6cd)

    • 现状实现(改前):read_upload_state / upload_chunk / complete_upload 都返回 Result<_, String>,4 个 HTTP 兜底全是 HTTP {status}。一次 upload_staged_game_package 会跨多请求:读上传状态 N+1 次、每个分片 PUT 最多 4 次、最后 complete 1 次,错误一路裸抛到 UI(commands/desktop.rs / game_distribution_publish.rs 都没补前缀),用户和模型看不出是哪一步挂的、下一步该做什么。
    • 已做:新增子枚举 GamePackageUploadStep(ReadUploadState / UploadChunk / CompleteUpload)和 GamePackageUploadError { step, detail };三个远端 helper 改返回 typed 错误,detail 只放服务端 message / code / HTTP {status} 这类事实,步骤名由 step 枚举统一渲染(to_user_msg)。
    • 与 review 建议的差别:review 建议把「发行包分片上传失败」写回叶子的兜底字符串;这里改成枚举字段承载。效果一样(消息仍是「发行包分片上传失败:HTTP 503」),但步骤是结构化字段,不是每个调用点硬拼的字符串。
    • 边界:upload_staged_game_package 仍返回 String——本地错误(暂存文件、体积不一致等)不是远端步骤,不硬塞进这个枚举;只在三个远端调用点 .map_err(|e| e.to_user_msg())。两处调用方零改动。
  • 5. game_package_upload/runtime.rs:155-160 — upload_chunk Fatal 分支同款顺序 bug(已修:1a99a2f26)

    • 现状实现(改前):message.or(code).filter(...)。
    • 问题:与 2/3 同型,空 message 吞掉 code。
    • 已做:与 3 同一提交修正。
  • 6. game_package_upload/runtime.rs:199-203 — complete_upload 同款顺序 bug(已修:1a99a2f26)

    • 现状实现(改前):message.or(code).filter(...)。
    • 问题:同上。
    • 已做:与 3/5 同一提交修正(三处是同一个 bug,合成一个提交;改的 4 个点里另含 upload_chunk retryable 分支)。
  • 7. agent/generation/canvas_generation.rs:1276-1277 — 去掉 phaseDetail 兜底会在这条路上丢信息(breaking,留给你)

    • 现状实现:远端 status=failed 时只读平台 error;没有就「服务器未返回错误信息」,不再读 phaseDetail。
    • 问题:这条函数返回 String,没有 typed 错误、也没有诊断 sidecar;平台只给 phaseDetail 不给 error 时(background_removal_tests.rs 的 fixture 就是这个形状),原因只留在「服务器未返回错误信息」里,诊断侧也看不到。
    • 与资源编辑链的差别:资源编辑那条把 phaseDetail 存进 ResourceEditError::RemoteGenerationFailed { phase_detail },虽然不给用户看,但会随 typed 错误进诊断;canvas 这条没有等价通道。
    • 建议(你来定):按你定的口径「phaseDetail 不是用户文案」,正确解是给这条也接上诊断记录(把 phaseDetail 写进 runtime error 诊断,而不是塞进用户 message);或者退一步,只在这条没有诊断通道的路径上保留 phaseDetail 兜底。我没有动,因为它要么动用户文案、要么要加诊断链路。
- [x] 1. `account_api.rs:81-90` — `error_code` 与 `error_message` 重复解析(已修:675dfe0c8) - 现状实现(改前):两个 fn 各自 `serde_json::from_str` + `get("error").unwrap_or(&value)`,只有取的字段名不同(`"message"` / `"code"`)。 - 问题:同一份「解包 error 信封」逻辑维护两遍,加字段就得改两处,容易漏。 - 已做:抽出 `error_field(body, field)`,`error_message` / `error_code` 变成两个薄封装;调用点不动,行为不变。 - [x] 2. `game_distribution_publish.rs:196-200` — `message.or(code)` 先于 `.filter`(已修:a0428da5d) - 现状实现(改前):`message.or(code).filter(|d| !d.trim().is_empty())`。`Option::or` 先求值,`Some("")` 会短路掉 `code`。 - 问题:服务端给 `{"error":{"code":"X","message":""}}` 这种「有空 message + 有 code」时,先选中空 message,再被 filter 丢掉,最后落到「服务器未返回错误信息」,恰好丢掉本次改动想带出的 code。 - 已做:改成 `message.filter(非空).or_else(|| code.filter(非空))`,每个候选先过滤再退下一个。 - 附带:同文件的 `map_http_error` 是同一个写法(`message.or(code).filter(...)`),review 没列,但属同一类 bug,一并修了(同提交)。 - [x] 3. `game_package_upload/runtime.rs:91-95` — `read_upload_state` 同款顺序 bug(已修:1a99a2f26) - 现状实现(改前):`message.or(code).filter(...).unwrap_or_else(|| format!("HTTP {status}"))`。 - 问题:与 2 完全同型;服务端给空 message + 有效 code 时 code 被吞,只剩裸 `HTTP {status}`。 - 已做:改成 `message.filter(...).or_else(|| code.filter(...))`。 - [x] 4. `game_package_upload/runtime.rs:146-151` — 三步共用裸 `HTTP {status}`,分不出是哪一步(已修:cdccfa6cd) - 现状实现(改前):`read_upload_state` / `upload_chunk` / `complete_upload` 都返回 `Result<_, String>`,4 个 HTTP 兜底全是 `HTTP {status}`。一次 `upload_staged_game_package` 会跨多请求:读上传状态 N+1 次、每个分片 PUT 最多 4 次、最后 complete 1 次,错误一路裸抛到 UI(`commands/desktop.rs` / `game_distribution_publish.rs` 都没补前缀),用户和模型看不出是哪一步挂的、下一步该做什么。 - 已做:新增子枚举 `GamePackageUploadStep`(`ReadUploadState` / `UploadChunk` / `CompleteUpload`)和 `GamePackageUploadError { step, detail }`;三个远端 helper 改返回 typed 错误,`detail` 只放服务端 message / code / `HTTP {status}` 这类事实,步骤名由 `step` 枚举统一渲染(`to_user_msg`)。 - 与 review 建议的差别:review 建议把「发行包分片上传失败」写回叶子的兜底字符串;这里改成枚举字段承载。效果一样(消息仍是「发行包分片上传失败:HTTP 503」),但步骤是结构化字段,不是每个调用点硬拼的字符串。 - 边界:`upload_staged_game_package` 仍返回 `String`——本地错误(暂存文件、体积不一致等)不是远端步骤,不硬塞进这个枚举;只在三个远端调用点 `.map_err(|e| e.to_user_msg())`。两处调用方零改动。 - [x] 5. `game_package_upload/runtime.rs:155-160` — `upload_chunk` Fatal 分支同款顺序 bug(已修:1a99a2f26) - 现状实现(改前):`message.or(code).filter(...)`。 - 问题:与 2/3 同型,空 message 吞掉 code。 - 已做:与 3 同一提交修正。 - [x] 6. `game_package_upload/runtime.rs:199-203` — `complete_upload` 同款顺序 bug(已修:1a99a2f26) - 现状实现(改前):`message.or(code).filter(...)`。 - 问题:同上。 - 已做:与 3/5 同一提交修正(三处是同一个 bug,合成一个提交;改的 4 个点里另含 `upload_chunk` retryable 分支)。 - [ ] 7. `agent/generation/canvas_generation.rs:1276-1277` — 去掉 phaseDetail 兜底会在这条路上丢信息(breaking,留给你) - 现状实现:远端 `status=failed` 时只读平台 `error`;没有就「服务器未返回错误信息」,不再读 `phaseDetail`。 - 问题:这条函数返回 `String`,没有 typed 错误、也没有诊断 sidecar;平台只给 `phaseDetail` 不给 `error` 时(`background_removal_tests.rs` 的 fixture 就是这个形状),原因只留在「服务器未返回错误信息」里,诊断侧也看不到。 - 与资源编辑链的差别:资源编辑那条把 `phaseDetail` 存进 `ResourceEditError::RemoteGenerationFailed { phase_detail }`,虽然不给用户看,但会随 typed 错误进诊断;canvas 这条没有等价通道。 - 建议(你来定):按你定的口径「phaseDetail 不是用户文案」,正确解是给这条也接上诊断记录(把 `phaseDetail` 写进 runtime error 诊断,而不是塞进用户 message);或者退一步,只在这条没有诊断通道的路径上保留 `phaseDetail` 兜底。我没有动,因为它要么动用户文案、要么要加诊断链路。
k88936 added 11 commits 2026-10-02 22:21:49 +08:00
- remove_background_payload 的恢复与派生两处不再套 with_direct_editor_api_credentials_as:整个 payload 已在 bridge_remove_background 的 with_direct_editor_api_credentials 作用域内,内层凭据解析只会命中 override
- 因此 ResourceEditError::Other 作为 credentials_error 的分支不可达,删掉以免误导(凭据失败仍按原路径落到 CredentialsUnavailable)
- 保留 Box::pin(资源编辑 future 体积问题不变),注释改为同时说明装箱原因与凭据作用域来源
- RemoteResourceEditFailure::to_user_msg 只回平台 error 原文,平台没给时统一说「服务器未返回错误信息」,不再拼一句没有信息量的总结
- 前缀交给使用它的工具自己加:生成/派生侧「生成或派生资源失败:」、抠图侧「抠图失败:」,两侧文案回到各自工具的既有口径
- 载体仍是两个工具共用的 RemoteResourceEditFailure,平台原文照旧整段进诊断的 typed error
- 补单测:载体层断言原文逐字透传与缺省兜底,工具层断言各自前缀下的文案
- ResourceEditError::to_user_msg 不再拼「资源编辑生成失败」:远端终态只回平台 error 原文,平台没给时统一说「服务器未返回错误信息」
- 桌面命令面 derive/resume_local_project_resource 自己补「资源编辑生成失败:」前缀,桌面 UI 文案不变,前缀落在它真正的使用者这一层
- 单测:typed 错误断言原文逐字透传与缺省兜底;抠图远端失败测试同口径更新
- decision-log 追加「前缀归属」决策(工具面各用各的前缀、桌面面用资源编辑生成失败、叶子错误不写死总结),pitfalls 同步口径
- canvas_generation 轮询到 status=failed 时,平台既没给 error 也没给 phaseDetail 的兜底由「生成任务失败」改为「服务器未返回错误信息」,不再和句首「平台图片生成任务失败:」重复
- decision-log 追加同类兜底决策,说明这条与资源编辑同属「缺省时要说清是服务器没给信息,而不是再喊一次失败」
RemoteResourceEditFailure / ResourceEditError 的 to_user_msg 只回平台 error 原文,平台没给时回「服务器未返回错误信息」
typed 错误新增 phaseDetail 字段,只随诊断 sidecar 序列化,不参与用户文案
远端轮询 failed 分支同时读取 error 与 phaseDetail
删除断言 to_user_msg 字面量的单测,只保留 typed 字段原样序列化进诊断的结构断言
canvas_generation 远端 failed 分支不再拿 phaseDetail 当用户文案
同步 decision-log 与 pitfalls
game_package_upload 三处不再丢弃服务端 code,改成 message → code → HTTP {status}
去掉「读取上传状态失败(HTTP 503)」这类叶子里的操作名前缀,操作名交给调用方话术
game_distribution_publish response_data 用上被丢掉的 error.code
game_distribution_publish 封面任务终态兜底统一成「服务器未返回错误信息」
account_api envelope 分支补 error.code 兜底
error_message / error_code 只差字段名,合并到 error_field(body, field)
调用点保留 error_message / error_code 两个薄封装
game_distribution_publish response_data 的 message.or(code).filter 顺序反了,空 message 会连 code 一起丢掉
map_http_error 同类顺序问题一并修正(review 未列,属同一类 bug)
read_upload_state / upload_chunk(retryable、fatal) / complete_upload 四处 .or(code).filter 顺序反了
空 message 会把服务端 code 一起吞掉,统一改成 filter(message).or_else(|| code.filter(...))
新增 GamePackageUploadStep(ReadUploadState / UploadChunk / CompleteUpload)与 GamePackageUploadError { step, detail }
read_upload_state / upload_chunk / complete_upload 改返回 typed 错误,detail 只放服务端 message / code / HTTP {status}
步骤名由 step 枚举渲染,不再靠各调用点硬拼「读取上传状态失败」这类前缀
upload_staged_game_package 在三个远端调用点 .map_err(to_user_msg);本地错误与对外签名保持 String,两处调用方零改动
Merge remote-tracking branch 'origin/master' into fix/res-edit-error-handling
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m1s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 3m51s
Project CI / Backend tests (pull_request) Successful in 4m43s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 4m48s
Project CI / Frontend tests (pull_request) Successful in 2m19s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m26s
Project CI / Repository checks (pull_request) Successful in 2m53s
Project CI / Native shell tests (pull_request) Successful in 6m5s
c0ad48798f
Author
Member
  • 1. game_package_upload/runtime.rs:158-161 —— 「message → code → HTTP 兜底」在 8 处复制粘贴

    • 现状:runtime.rs 的 5 个 HTTP 错误分支(read_upload_state、upload_chunk 的 409 / 5xx-408-429 / 其它 4xx、complete_upload),加上 game_distribution_publish.rs 的 response_data 与 map_http_error,各自复制同一段 message.filter(非空).or_else(code.filter(非空)).unwrap_or(兜底)。
    • 问题:顺序是隐式约定,已经发生漂移——map_http_error 的 FORBIDDEN 分支仍是 message.unwrap_or_else(...),没有空串过滤,message=Some("") 时会输出 permission-denied: (附了空原因)。
    • 建议/处理:在 game_package_upload.rs 紧跟 parse_server_error 新增 pub(crate) fn server_error_detail(code, message, fallback),8 处统一改用它;FORBIDDEN 分支同时补上 trim 过滤(兜底文案仍是「当前账号无权执行此操作」)。
    • 提交:b3ed1c0f8
  • 2. game_package_upload/runtime.rs:123-130 —— detail 与步骤标签语义重复

    • 现状:detail 里带着「读取响应失败」「上传状态响应不是合法 JSON」「分片响应缺少 receivedBytes」「完成响应不是合法 JSON」等主语,而 to_user_msg 又会再拼上 step 标签。
    • 问题:最终文案出现双层前缀,例如 读取上传状态失败:读取响应失败:{error}、完成发行包上传失败:完成响应不是合法 JSON:{error};「哪一步」已经由 GamePackageUploadStep 承担,detail 再重复一遍是噪音。
    • 建议/处理:能由 step 表达的主语一律从 detail 删除——响应读取失败只留 {error},JSON 解析/缺字段改为「响应不是合法 JSON:{error}」「响应缺少字段:{error}」「响应缺少 receivedBytes」;保留「请求未送达:{error}」「无法连接登录服务…」这类 step 没覆盖的事实。
    • 提交:b4bbd0841
  • 3. game_package_upload/runtime.rs:94-98 —— 结构化错误在边界被反复手动 flatten 成 String

    • 现状:GamePackageUploadError(step + detail,且 derive Serialize)只在 upload_staged_game_package 里用 5 次 .map_err(|e| e.to_user_msg()) 拍平成 String,Serialize / step 结构没有进入任何序列化或日志路径。
    • 问题:每个边界都要手写一次 flatten,重复且容易漏;结构只换来一个标签前缀。
    • 建议/处理:采用 review 的「轻量」方案而不是它贴的「删掉整个 struct」——保留结构化字段(后续诊断要序列化它,之前已定过这个方向),只补 impl From<GamePackageUploadError> for String,把三处 .map_err(|e| e.to_user_msg()) 与 Fatal 分支改成 ? / error.into();重试次数那条需要拼后缀,继续用 to_user_msg()。
    • 提交:fb80c7d5e
  • 4. agent/generation/canvas_generation.rs:1276-1277 —— 已确认不修(无意义)

    • 查证:phaseDetail 是后端固定的阶段展示文案,不是错误原因,failed 恒为「生成失败。」(external_generation.rs:181、editor_generation_queue.rs:706 两处固定映射;external_generation.rs:422/452 与 AGC 夹具 background_removal_tests.rs:691 佐证)。
    • 结论:把它并回客户端用户文案只会得到「平台图片生成任务失败:生成失败。」这类重复且不可行动的句子,改客户端无意义。缺原因属后端 error 在 last_error_message 为空时不落值的问题,真要治在后端,另行处理。
- [x] 1. game_package_upload/runtime.rs:158-161 —— 「message → code → HTTP 兜底」在 8 处复制粘贴 - 现状:runtime.rs 的 5 个 HTTP 错误分支(read_upload_state、upload_chunk 的 409 / 5xx-408-429 / 其它 4xx、complete_upload),加上 game_distribution_publish.rs 的 response_data 与 map_http_error,各自复制同一段 `message.filter(非空).or_else(code.filter(非空)).unwrap_or(兜底)`。 - 问题:顺序是隐式约定,已经发生漂移——map_http_error 的 FORBIDDEN 分支仍是 `message.unwrap_or_else(...)`,没有空串过滤,`message=Some("")` 时会输出 `permission-denied: `(附了空原因)。 - 建议/处理:在 game_package_upload.rs 紧跟 parse_server_error 新增 `pub(crate) fn server_error_detail(code, message, fallback)`,8 处统一改用它;FORBIDDEN 分支同时补上 trim 过滤(兜底文案仍是「当前账号无权执行此操作」)。 - 提交:b3ed1c0f8 - [x] 2. game_package_upload/runtime.rs:123-130 —— detail 与步骤标签语义重复 - 现状:detail 里带着「读取响应失败」「上传状态响应不是合法 JSON」「分片响应缺少 receivedBytes」「完成响应不是合法 JSON」等主语,而 to_user_msg 又会再拼上 step 标签。 - 问题:最终文案出现双层前缀,例如 `读取上传状态失败:读取响应失败:{error}`、`完成发行包上传失败:完成响应不是合法 JSON:{error}`;「哪一步」已经由 GamePackageUploadStep 承担,detail 再重复一遍是噪音。 - 建议/处理:能由 step 表达的主语一律从 detail 删除——响应读取失败只留 `{error}`,JSON 解析/缺字段改为「响应不是合法 JSON:{error}」「响应缺少字段:{error}」「响应缺少 receivedBytes」;保留「请求未送达:{error}」「无法连接登录服务…」这类 step 没覆盖的事实。 - 提交:b4bbd0841 - [x] 3. game_package_upload/runtime.rs:94-98 —— 结构化错误在边界被反复手动 flatten 成 String - 现状:GamePackageUploadError(step + detail,且 derive Serialize)只在 upload_staged_game_package 里用 5 次 `.map_err(|e| e.to_user_msg())` 拍平成 String,Serialize / step 结构没有进入任何序列化或日志路径。 - 问题:每个边界都要手写一次 flatten,重复且容易漏;结构只换来一个标签前缀。 - 建议/处理:采用 review 的「轻量」方案而不是它贴的「删掉整个 struct」——保留结构化字段(后续诊断要序列化它,之前已定过这个方向),只补 `impl From<GamePackageUploadError> for String`,把三处 `.map_err(|e| e.to_user_msg())` 与 Fatal 分支改成 `?` / `error.into()`;重试次数那条需要拼后缀,继续用 to_user_msg()。 - 提交:fb80c7d5e - [x] 4. agent/generation/canvas_generation.rs:1276-1277 —— 已确认不修(无意义) - 查证:phaseDetail 是后端固定的阶段展示文案,不是错误原因,failed 恒为「生成失败。」(`external_generation.rs:181`、`editor_generation_queue.rs:706` 两处固定映射;`external_generation.rs:422/452` 与 AGC 夹具 `background_removal_tests.rs:691` 佐证)。 - 结论:把它并回客户端用户文案只会得到「平台图片生成任务失败:生成失败。」这类重复且不可行动的句子,改客户端无意义。缺原因属后端 `error` 在 `last_error_message` 为空时不落值的问题,真要治在后端,另行处理。
k88936 added 4 commits 2026-10-03 11:21:48 +08:00
- game_package_upload.rs 新增 pub(crate) server_error_detail,统一先取非空 message 再退非空 code 的顺序
- runtime.rs 五处 HTTP 错误分支改用该 helper,删除各自复制的过滤逻辑
- game_distribution_publish.rs 的 response_data 与 map_http_error 共用同一 helper
- FORBIDDEN 分支改用 helper,补上此前缺失的空 message trim 过滤
- 读取响应/分片响应/完成响应失败改为直接给原始 error,步骤由 GamePackageUploadStep 标签提供
- JSON 解析与缺字段的 detail 去掉「上传状态响应」「分片响应」「完成响应」等重复主语
- 为 GamePackageUploadError 实现 From<GamePackageUploadError> for String
- upload_staged_game_package 中三处 .map_err(|e| e.to_user_msg()) 改为 ?,Fatal 分支改为 error.into()
Merge remote-tracking branch 'origin/master' into fix/res-edit-error-handling
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m43s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m4s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 4m9s
Project CI / Backend tests (pull_request) Successful in 4m39s
Project CI / Frontend tests (pull_request) Successful in 2m19s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m20s
Project CI / Repository checks (pull_request) Successful in 3m2s
Project CI / Native shell tests (pull_request) Successful in 5m37s
bb446553fd
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
k88936 merged commit c7e51266c8 into master 2026-10-03 11:32:40 +08:00
k88936 deleted branch fix/res-edit-error-handling 2026-10-03 11:32:41 +08:00
Sign in to join this conversation.