Claude Code 错误处理模式:在边界分类失败并设计恢复路径
用 Claude Code 设计可维护的错误处理。涵盖 API 校验、外部 API 失败、批处理失败与 TypeScript 实现。
错误处理不是到处添加try-catch。如果只是让 Claude Code“把报错修一下”,它很容易把所有失败都变成500,或者在日志里留下难以检索的字符串。真正有用的设计,是在系统边界先判断失败的种类,再写清楚恢复方式。
这里的边界,指的是应用与外部世界接触的位置:API 请求体、第三方支付、CRM、邮件服务、定时任务、CSV 导入、前端页面等。边界越清楚,Claude Code 越容易做正确的修改。例如,用户输入错误返回400;第三方服务临时不可用可以重试;批处理失败要留下可重跑的记录。
本文把错误处理解释成一个适合初学者的模式:在边界分类失败,并明确恢复路径。你会看到可复制运行的 TypeScript 示例、三个真实用例、常见坑、Claude Code 审查提示词,以及面向培训和商品转化的 CTA。
先从恢复方式倒推错误类型
不要先纠结类名叫AppError还是DomainError。先问:系统下一步应该怎么做?
| 边界 | 常见失败 | 返回方式 | 恢复方式 |
|---|---|---|---|
| API 输入校验 | 邮箱格式错误、年龄越界、JSON 结构错误 | 400 | 让用户修正输入 |
| 外部 API | 支付、CRM、邮件供应商不可用 | 502或503 | 重试、暂停流程、提示临时失败 |
| Job/Batch | 夜间报表、CSV 导入、通知发送失败 | 内部失败记录 | 安全重跑、失败队列、人工通知 |
只保存message是不够的。人能读懂,但程序很难做判断。建议至少保留kind、code、retryable、status。这样 Claude Code 在审查时可以检查行为,而不是猜测字符串含义。
可复制运行的 TypeScript 示例
下面的示例可以在 Node.js 18 以上运行。它没有真正访问外部网络,而是注入一个假的fetcher来稳定复现失败。
npm install -D tsx typescript
npx tsx error-patterns-demo.ts
type Kind = "validation" | "external" | "job";
type AppError = { kind: Kind; code: string; message: string; retryable: boolean; status: number; detail?: unknown };
type Result<T> = { ok: true; value: T } | { ok: false; error: AppError };
const ok = <T>(value: T): Result<T> => ({ ok: true, value });
const fail = <T>(error: AppError): Result<T> => ({ ok: false, error });
function parseUser(body: unknown): Result<{ email: string; age: number }> {
if (typeof body !== "object" || body === null) {
return fail({ kind: "validation", code: "BODY_REQUIRED", message: "body must be an object", retryable: false, status: 400 });
}
const data = body as Record<string, unknown>;
if (typeof data.email !== "string" || !data.email.includes("@")) {
return fail({ kind: "validation", code: "EMAIL_INVALID", message: "email is invalid", retryable: false, status: 400 });
}
if (typeof data.age !== "number" || data.age < 13) {
return fail({ kind: "validation", code: "AGE_INVALID", message: "age must be 13 or greater", retryable: false, status: 400 });
}
return ok({ email: data.email, age: data.age });
}
async function callPartner(fetcher: () => Promise<Response>): Promise<Result<{ id: string }>> {
try {
const response = await fetcher();
if (!response.ok) {
return fail({ kind: "external", code: "PARTNER_HTTP", message: `partner returned ${response.status}`, retryable: response.status >= 500, status: 503 });
}
const json = (await response.json()) as { id?: unknown };
if (typeof json.id !== "string") {
return fail({ kind: "external", code: "PARTNER_PAYLOAD", message: "partner payload is invalid", retryable: false, status: 502, detail: json });
}
return ok({ id: json.id });
} catch (error) {
return fail({ kind: "external", code: "PARTNER_UNREACHABLE", message: "partner is unreachable", retryable: true, status: 503, detail: error });
}
}
async function runJob<T>(name: string, work: () => Promise<T>, retries = 2): Promise<Result<T>> {
for (let attempt = 1; attempt <= retries + 1; attempt += 1) {
try {
return ok(await work());
} catch (error) {
if (attempt <= retries) continue;
return fail({ kind: "job", code: "JOB_FAILED", message: `${name} failed`, retryable: true, status: 500, detail: { attempt, error } });
}
}
return fail({ kind: "job", code: "JOB_FAILED", message: `${name} failed`, retryable: true, status: 500 });
}
console.log(parseUser({ email: "bad", age: 10 }));
console.log(await callPartner(async () => new Response("down", { status: 503 })));
console.log(await runJob("daily-report", async () => ({ exportedRows: 42 })));
用例1: API 输入校验
API 校验失败通常是用户可以修正的失败。邮箱格式不对、年龄太小、JSON 不是对象、套餐名称不存在,这些都不应该被包装成服务器崩溃。示例里的parseUser在请求进入业务逻辑之前就返回validation,并设置status: 400和retryable: false。
这会带来三个好处。前端可以根据字段提示用户;后端日志可以用EMAIL_INVALID搜索;Claude Code 可以围绕失败路径补测试,而不是只测正常路径。和Claude Code API 测试指南一起使用时,输入边界会更稳定。
用例2: 外部 API 失败
外部服务即使在你的代码正确时也会失败。支付、邮件、CRM、Google Sheets、Slack 都可能超时、限流或返回不符合约定的数据。这里必须区分“可以重试”和“不应该重试”。
示例里的callPartner把5xx类失败标记为retryable: true,但把响应结构错误标记为retryable: false。如果对错误结构无限重试,只会制造更多日志噪音。判断 HTTP 响应时可以参考官方的MDN Response.ok。如果项目使用 Express,也应确认Express error handling里关于错误中间件位置的说明。
用例3: Job/Batch 失败
批处理失败的危险在于,用户往往已经离开页面。夜间报表、CSV 导入、账单生成、通知发送都需要留下可重跑的线索。runJob记录了任务名、最终尝试次数和可重试性。真实项目里,这些信息应该进入结构化日志、失败队列、管理后台或告警系统。
最重要的问题是幂等性。幂等性是指同一个操作重复执行也不会造成额外副作用。如果 CSV 导入会重复插入数据,或者账单任务会重复扣款,那么重试逻辑本身就是风险。让 Claude Code 检查“能否安全重跑”,比只检查语法更重要。
常见坑与 Claude Code 审查提示词
第一个坑是catch { return null; }。这会把证据删掉,后续无法追查。第二个坑是把 SQL 名称、环境变量、堆栈、密钥片段返回给用户。安全层面的检查可以参考Claude Code 安全审计自动化。第三个坑是对所有失败都重试。校验错误和错误响应结构通常不会因为等待而变好。
可以把下面的提示词交给 Claude Code:
请审查这个 PR 的错误处理。
重点:
1. 是否在 API 输入、外部 API、Job/Batch 边界分类失败
2. 用户可修正的失败是否错误地返回 500
3. retryable 和 non-retryable 是否分开
4. 响应是否泄露 stack trace、SQL、密钥或内部路径
5. 日志是否包含 code、kind、attempt、cause
6. 是否至少覆盖 3 个失败路径测试
请给出最小修改和对应测试。
TypeScript 的判别联合与类型收窄可以参考官方TypeScript Narrowing,失败路径测试可以参考Node.js test runner。如果想用测试先固定失败,再让 Claude Code 修复,可以继续阅读Claude Code TDD 实践和调试技巧指南。Claude Code 本身的项目记忆与设置可从Claude Code overview确认。
CTA 与 Masa 的试用结果
错误处理非常适合做成团队培训,因为它连接了 API、日志、安全、测试和运维。如果你想先自己试,可以从免费速查表开始。需要提示词、审查清单和模板时,可以看商品列表。如果要把既有项目的错误处理整理成 Claude Code 可维护的规则,可以通过Claude Code 培训与导入咨询一起梳理。
Masa 在自己的项目里把输入校验、外部 API、批处理失败统一到类似AppError的形状后,给 Claude Code 的指示明显变短了。以前会写“把这个错误修好”,现在可以写“validation 返回 400,external 根据 retryable 判断是否重试,job 保留 attempt”。审查范围也自然收敛到响应、日志、测试三处。它不是万能架构,但比到处都是匿名500更容易运营。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code Permission Receipt Pattern:记录权限、证据和回滚方式
Claude Code 权限 receipt:记录允许动作、需要批准的边界、验证命令、回滚说明,以及 Gumroad 和咨询 CTA 检查。
Claude Code/Codex 安全 Agent Harness 实战:权限、验证与回滚
用权限策略、执行计划、验证脚本和回滚日志,为 Claude Code 与 Codex 搭建更安全的 AI Agent 工作流。
Claude Code 子代理实战指南:安全委派并行文章与代码工作
用 Claude Code 子代理安全拆分文章和代码工作:委派规则、提示词模板、失败模式与检查清单。