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 只阻止了异常传播,没有撤销已经发生的状态变化。
三条原则
- 类型化、结构化的失败。预期的失败有声明的类型、稳定的判别字段和处理所需的细节。保留 cause,把机器标识和展示文字分开。不通过解析错误消息来路由处理逻辑。
- 捕获、恢复或停止。每种失败都要在 API 文档里写明处理和报告的 owner、隔离范围,以及失败后的保证:哪些不变量仍然成立,效果是不存在、部分、已提交还是未知,谁负责清理。重试需要能安全重复、可取消、有明确 owner,有限的操作要有预算。
- 本地化、可操作、可见的报告。最终报告包含可读的摘要、受影响的操作和安全的下一步。永远不在没有保证时宣称成功、已回滚或可安全重试。报告本身失败时要有独立的后备路径。
派发器隔离回调异常
一个监听器抛异常,不能让整个派发的 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 关于隔离与监督的论文。