跳转至

错误处理全景

重要性:⭐⭐⭐(错误全景——3 层 + 命名约定 + 安全 + 重试 + 优雅降级) 范围src/ 错误处理相关 方法:基于 analysis/error-handling.md 已有基础 + 跨文件梳理


1. 3 层错误处理

┌─────────────────────────────────────┐
│  Layer 1: 抛出(throw)              │  ← JS 原生
├─────────────────────────────────────┤
│  Layer 2: 命名错误类                │  ← 业务层
├─────────────────────────────────────┤
│  Layer 3: 安全错误类                │  ← 监控层
└─────────────────────────────────────┘

3 层递进


2. Layer 1: 抛出(throw)

throw new Error('msg')

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 命名约定

TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS

命名约定 —— 作者显式声明该错误可安全上报。

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

try {
  await operation();
} catch (e) {
  logError(e);
  return null;
}

最常见 —— "出错就 log"。

5.2 try/catch + rethrow

try {
  await operation();
} catch (e) {
  logError(e);
  throw new McpAuthError('重新认证');
}

包装 —— 内部错误 → 业务错误。

5.3 try/catch + fallback

try {
  return await fastOperation();
} catch (e) {
  return await slowFallback();
}

降级 —— 失败用备用方案。

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

// pending401Handlers
const pending401Handlers = new Map<string, Promise<boolean>>()

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

void loadRemoteManagedSettings()  // 失败 open
void loadPolicyLimits()  // 失败 open

fire-and-forget —— 失败不阻塞启动。

7.4 Prefetch

if (isEnvTruthy(process.env.CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER) || isBareMode()) {
  return;  // 跳过预取
}

可关闭 —— 用户可控。


8. 错误处理与 logging

8.1 logError

logError(e)  // 全局 error logger

统一 logging —— 所有错误走 logError

8.2 logEvent

logEvent('tengu_managed_settings_loaded', { ... })

遥测 —— 业务事件(非错误)。

8.3 Statsig 上报

profileReport()  // 启动 metrics

性能 metrics —— 单独通道。


9. 错误处理与 UX

9.1 友好错误信息

function enhanceGitPullErrorMessages(result: ...): ... {
  // 把 git stderr 转成可读错误
}

错误翻译 —— git fatal: ... → 友好提示。

9.2 错误信息脱敏

function redactUrlCredentials(url: string): string {
  // https://user:pass@github.com → https://***:***@github.com
}

不暴露密码

9.3 错误信息截断

const MAX_MCP_DESCRIPTION_LENGTH = 2048

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

process.on('exit', () => {
  resetCursor()
})

退出时 —— 重置光标。


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 基类

class ClaudeError extends Error { ... }

统一基类 —— 顶层 catch 一把抓。

15.2 错误码系统

enum ErrorCode { ... }

错误码 —— 国际化 / 文档化。

15.3 完整 retry 库

retry(operation, { maxAttempts, backoff })

重试抽象 —— 替代散落实现。


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. 阅读建议

  1. utils/errors.ts —— 基础错误
  2. mcp/auth.ts 的错误类 —— 业务错误示例
  3. attachments.ts 的 maybe() —— 优雅降级
  4. auth.ts 的 pending401Handlers —— 重试模式

18. 与其他分析的关系

文件 关系
security-audit.md 错误 vs 安全
architecture-history.md 错误处理演进
extensibility.md 扩展点错误处理