Skip to content

08 · 错误处理

谁能恢复,怎样报告 ​

关键文件.agents/skills/dsh-error-handling/SKILL.mddocs/defensive-patterns/general.md

模型写错误处理时最常见的 slop 是 try { … } catch (e) { console.error(e) } 然后继续执行。dsh-error-handling 的回答是:"Catching limits exception propagation, not mutations." catch 只阻止了异常传播,没有撤销已经发生的状态变化。

catch 后只打日志就继续:不算恢复常见错误发生失败类型化错误稳定判别字段 + cause受影响状态的 owner能证明后续前提成立?能不能恢复并继续停止依赖工作丢弃状态或上报可见报告:摘要 · 受影响的操作 · 安全的下一步
只有状态的 owner 能决定继续。其他位置的 catch 只能停止依赖它的工作、丢弃无效状态或向上报告。不可信的状态和未知的结果必须通过 UI 或默认开启的日志可见,并说明影响、不确定性和安全的下一步。

三条原则 ​

  1. 类型化、结构化的失败。预期的失败有声明的类型、稳定的判别字段和处理所需的细节。保留 cause,把机器标识和展示文字分开。不通过解析错误消息来路由处理逻辑。
  2. 捕获、恢复或停止。每种失败都要在 API 文档里写明处理和报告的 owner、隔离范围,以及失败后的保证:哪些不变量仍然成立,效果是不存在、部分、已提交还是未知,谁负责清理。重试需要能安全重复、可取消、有明确 owner,有限的操作要有预算。
  3. 本地化、可操作、可见的报告。最终报告包含可读的摘要、受影响的操作和安全的下一步。永远不在没有保证时宣称成功、已回滚或可安全重试。报告本身失败时要有独立的后备路径。

派发器隔离回调异常 ​

一个监听器抛异常,不能让整个派发的 promise 失败,也不能让后面的监听器饿死:

ts
async function dispatch(listeners: ((event: string) => Promise<void>)[], event: string): Promise<void> {
  for (const listener of listeners) {
    try { await listener(event) } catch (error) { console.warn('listener failed', error) }
  }
}

注意配套的限制:派发器 catch 了,不代表状态已经恢复。监听器修改的状态仍然由监听器的 owner 负责。

这条 skill 末尾列出了理论来源:Meyer 的契约与不变量、Sutter 的异常安全保证、Armstrong 关于隔离与监督的论文。

依据 deepseek-harness 仓库 master 分支及其 git 历史整理