Skip to content

onErrorOccurred Hook 在 Agent 会话发生错误时触发,可以记录错误详情、向 AI 提供处理建议、触发告警通知。返回 null 使用默认错误处理,返回自定义对象可以修改错误响应方式。

GitHub Copilot SDK onErrorOccurred Hook:自定义错误处理与监控

基本结构

typescript
const session = await createSession({
  hooks: {
    onErrorOccurred: async (context) => {
      const { error, toolName, sessionId } = context
      
      // 错误处理逻辑
      return null  // 使用默认错误处理
    }
  }
})

错误日志记录

typescript
onErrorOccurred: async ({ error, toolName, sessionId }) => {
  // 结构化日志,便于后续分析
  errorLogger.log({
    timestamp: new Date().toISOString(),
    sessionId,
    toolName: toolName ?? 'session',
    errorCode: error.code,
    errorMessage: error.message,
    stack: error.stack
  })
  
  return null  // 让 AI 自己处理错误
}

按错误类型分类处理

typescript
onErrorOccurred: async ({ error, toolName }) => {
  switch (error.code) {
    case 'TIMEOUT':
      // 超时:提示 AI 换更高效的方式
      return {
        userMessage: `工具 "${toolName}" 执行超时,请尝试分批处理或缩小操作范围。`
      }
    
    case 'PERMISSION_DENIED':
      // 权限不足:直接告知
      return {
        userMessage: `没有权限执行 "${toolName}",请检查当前用户的权限配置。`
      }
    
    case 'NETWORK_ERROR':
      // 网络错误:可以重试
      return {
        userMessage: '网络连接异常,请稍后重试或检查网络配置。',
        retry: true  // 部分 SDK 版本支持自动重试提示
      }
    
    default:
      return null  // 其他错误用默认处理
  }
}

触发告警

对于需要人工介入的严重错误,触发外部告警:

typescript
onErrorOccurred: async ({ error, sessionId, toolName }) => {
  const isCritical = ['DISK_FULL', 'OUT_OF_MEMORY', 'DB_CONNECTION_FAILED'].includes(error.code)
  
  if (isCritical) {
    // 非阻塞告警(不 await)
    alerting.notify({
      level: 'critical',
      message: `Copilot SDK 严重错误: ${error.code}`,
      sessionId,
      tool: toolName
    }).catch(e => console.error('告警发送失败:', e))
  }
  
  // 记录所有错误的统计
  metrics.increment('sdk.errors', 1, { code: error.code, tool: toolName ?? 'session' })
  
  return null
}

错误模式分析

typescript
const errorCounts = new Map<string, number>()

onErrorOccurred: async ({ error }) => {
  // 统计错误频率
  const count = (errorCounts.get(error.code) ?? 0) + 1
  errorCounts.set(error.code, count)
  
  // 某个错误频繁出现时,给 AI 提供针对性建议
  if (error.code === 'FILE_NOT_FOUND' && count > 3) {
    return {
      userMessage: '多次出现文件未找到的错误,建议先用 list_directory 工具确认目录结构,再操作具体文件。'
    }
  }
  
  return null
}

最佳实践

1. 总是记录错误:即使使用默认处理,也要记录错误日志。没有日志,就无法分析问题模式。

2. 区分工具错误和会话错误toolName 有值时是工具执行失败,为 null 时是会话级别的错误(通常更严重)。

3. 避免 Hook 本身抛出异常:如果错误处理 Hook 本身出错,会造成双重错误,难以排查。在 Hook 内部做 try-catch:

typescript
onErrorOccurred: async (context) => {
  try {
    await errorLogger.log(context)
  } catch (loggingError) {
    // 静默处理日志错误,不向上传播
    console.error('错误日志记录失败:', loggingError)
  }
  return null
}

常见问题

Q: onErrorOccurred 和 session.on('session.error') 有什么区别?

A: session.on('session.error') 是事件监听,只能被动接收;onErrorOccurred Hook 可以修改错误的处理方式(如提供 userMessage)。通常两者配合使用:Hook 做处理,事件监听做 UI 更新。

Q: 返回 userMessage 后,AI 会怎么处理这个消息?

A: userMessage 会作为工具执行的错误响应返回给 AI,AI 会基于这个信息决定下一步。如果消息清晰,AI 通常能自动调整策略。

Q: 可以在 Hook 中重试工具调用吗?

A: Hook 本身不能直接触发重试。可以在 userMessage 中提示 AI 重试,让 AI 自主决定是否再次调用工具。