错误处理全景¶
重要性:⭐⭐⭐(错误全景——3 层 + 命名约定 + 安全 + 重试 + 优雅降级) 范围:
src/错误处理相关 方法:基于analysis/error-handling.md已有基础 + 跨文件梳理
1. 3 层错误处理¶
┌─────────────────────────────────────┐
│ Layer 1: 抛出(throw) │ ← JS 原生
├─────────────────────────────────────┤
│ Layer 2: 命名错误类 │ ← 业务层
├─────────────────────────────────────┤
│ Layer 3: 安全错误类 │ ← 监控层
└─────────────────────────────────────┘
3 层递进。
2. Layer 1: 抛出(throw)¶
JS 原生 throw —— 任何地方都可用。
问题: - 无类型信息 - 无可追踪性 - 无安全标记
3. Layer 2: 命名错误类¶
3.1 业务错误类¶
| 错误类 | 文件 | 触发 |
|---|---|---|
McpAuthError |
mcp/auth.ts | MCP 鉴权失败 |
McpSessionExpiredError |
mcp/auth.ts | MCP session 过期 |
AuthenticationCancelledError |
mcp/auth.ts | 用户取消 OAuth |
GcpCredentialsTimeoutError |
auth.ts | GCP 5s 超时 |
FileTooLargeError |
readFileInRange.ts | 文件过大 |
MaxFileReadTokenExceededError |
FileReadTool | token 超限 |
TeleportOperationError |
teleport.ts | teleport 失败 |
7+ 个命名错误类 —— 业务可 catch。
3.2 错误类的作用¶
try {
await mcpOperation();
} catch (e) {
if (e instanceof McpAuthError) {
// 触发 OAuth 重新认证
} else if (e instanceof McpSessionExpiredError) {
// reconnect
} else {
// 其他错误
}
}
类型化 catch —— 业务可针对错误类型处理。
4. Layer 3: 安全错误类¶
4.1 命名约定¶
命名约定 —— 作者显式声明该错误可安全上报。
4.2 McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS¶
export class McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
extends TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS { ... }
安全 —— 错误信息不含代码/路径。
4.3 价值¶
没有这个约定: - 错误信息可能含 API key / 路径 / 用户数据 - 上报到 telemetry 会泄露
有这个约定: - 作者必须审查错误信息 - 命名后缀 是契约
5. 错误处理模式¶
5.1 try/catch + log¶
最常见 —— "出错就 log"。
5.2 try/catch + rethrow¶
包装 —— 内部错误 → 业务错误。
5.3 try/catch + fallback¶
降级 —— 失败用备用方案。
5.4 maybe() helper¶
async function maybe<A>(label: string, f: () => Promise<A[]>): Promise<A[]> {
try {
return await f();
} catch (e) {
logError(e);
return [];
}
}
5 行 helper —— "失败不阻断"。
6. 重试机制¶
6.1 自定义重试¶
// gitPull, gitClone 等有重试逻辑
async function gitClone(url, dest, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await actualClone(url, dest);
} catch (e) {
if (i === retries - 1) throw e;
}
}
}
3 次重试 —— 网络抖动容错。
6.2 OAuth refresh¶
401 → refresh —— 自动重试。
6.3 MCP reconnect¶
reconnectMcpServerImpl —— 显式 reconnect API。
7. 优雅降级¶
7.1 附件系统¶
const userAttachmentResults = await Promise.all([
maybe('at_mentioned_files', () => processAtMentionedFiles(input, context)),
// ...
])
任何一个失败 → 返回空 → 不影响其他附件。
7.2 Plugin 加载¶
async function finishLoadingPluginFromPath(...): Promise<LoadedPlugin> {
try {
// 加载
} catch (e) {
// 跳过该 plugin
return null;
}
}
宽容失败 —— 一个 plugin 坏不全盘崩。
7.3 Remote settings¶
fire-and-forget —— 失败不阻塞启动。
7.4 Prefetch¶
if (isEnvTruthy(process.env.CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER) || isBareMode()) {
return; // 跳过预取
}
可关闭 —— 用户可控。
8. 错误处理与 logging¶
8.1 logError¶
统一 logging —— 所有错误走 logError。
8.2 logEvent¶
遥测 —— 业务事件(非错误)。
8.3 Statsig 上报¶
性能 metrics —— 单独通道。
9. 错误处理与 UX¶
9.1 友好错误信息¶
错误翻译 —— git fatal: ... → 友好提示。
9.2 错误信息脱敏¶
function redactUrlCredentials(url: string): string {
// https://user:pass@github.com → https://***:***@github.com
}
不暴露密码。
9.3 错误信息截断¶
2KB 截断 —— 防 prompt 注入。
10. AbortController 模式¶
10.1 1s getAttachments¶
const abortController = createAbortController()
const timeoutId = setTimeout(ac => ac.abort(), 1000, abortController)
const context = { ...toolUseContext, abortController }
总 1s 超时 —— 附件拼装。
10.2 其他 abort¶
- 50ms bash parse
- 30s MCP auth
- 3 min AWS / GCP refresh
多种 timeout —— 保护主线程。
11. 信号处理¶
11.1 SIGINT¶
// main.tsx
process.on('SIGINT', () => {
if (process.argv.includes('-p') || process.argv.includes('--print')) {
return // 让 print.ts 处理
}
process.exit(0)
})
Ctrl+C —— 优雅退出。
11.2 exit¶
退出时 —— 重置光标。
12. 进程退出码¶
| 退出码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 一般错误 |
| 130 | SIGINT (Ctrl+C) |
3 种 —— 标准实践。
13. 错误处理"成熟度"评分¶
| 模式 | 评分 | 备注 |
|---|---|---|
| 命名错误类 | ⭐⭐⭐⭐ | 7+ 类 |
| 安全错误类 | ⭐⭐⭐⭐⭐ | 命名约定强制 |
| 重试机制 | ⭐⭐⭐⭐ | 401 refresh, git retry |
| 优雅降级 | ⭐⭐⭐⭐⭐ | 5+ 模式 |
| 错误脱敏 | ⭐⭐⭐⭐ | URL / header |
| 错误翻译 | ⭐⭐⭐⭐ | git / oauth |
| AbortController | ⭐⭐⭐⭐⭐ | 5+ timeout |
| 信号处理 | ⭐⭐⭐ | SIGINT + exit |
总体 ⭐⭐⭐⭐ — 商业级错误处理。
14. 错误处理的"反模式"检查¶
| 反模式 | 项目里有吗 |
|---|---|
try { } catch (e) {} 吞错 |
❌ 少见 |
console.error(e) 散落 |
⚠️ 偶有 |
throw new Error('generic') |
⚠️ 老代码 |
| 错误信息含敏感数据 | ❌ 安全类强制 |
| 错误不传播 | ❌ 多数重 throw |
| 静默失败 | ⚠️ maybe() 故意 |
总体 —— 错误处理成熟。
15. 改进方向¶
15.1 统一 Error 基类¶
统一基类 —— 顶层 catch 一把抓。
15.2 错误码系统¶
错误码 —— 国际化 / 文档化。
15.3 完整 retry 库¶
重试抽象 —— 替代散落实现。
16. 关键洞察¶
16.1 3 层递进¶
throw → 命名类 → 安全类 —— 成熟度递增。
16.2 命名约定强制安全¶
_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS —— 类型级契约。
16.3 优雅降级是关键¶
5+ 模式保证 "失败不阻断"。
16.4 重试集中在 401¶
pending401Handlers —— 防止 refresh storm。
16.5 AbortController 普遍¶
5+ timeout 保护主线程。
16.6 错误处理成熟度 ⭐⭐⭐⭐¶
商业级 —— 不是 demo 级。
16.7 已知反模式¶
静默失败、console.error 散落 —— 但不严重。
17. 阅读建议¶
- 看
utils/errors.ts—— 基础错误 - 看
mcp/auth.ts的错误类 —— 业务错误示例 - 看
attachments.ts的 maybe() —— 优雅降级 - 看
auth.ts的 pending401Handlers —— 重试模式
18. 与其他分析的关系¶
| 文件 | 关系 |
|---|---|
security-audit.md |
错误 vs 安全 |
architecture-history.md |
错误处理演进 |
extensibility.md |
扩展点错误处理 |