zhin-error-handling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Zhin Error Handling Guide

Zhin错误处理指南

Use this skill to handle errors in Zhin plugins using the built-in error classes and resilience patterns.
本技能介绍如何使用内置错误类和弹性模式处理Zhin插件中的错误。

Error Class Hierarchy

错误类层级

All custom errors extend
ZhinError
:
ZhinError (base)
├── ConfigError         — Configuration issues
├── PluginError         — Plugin loading/execution failures
├── AdapterError        — Adapter/bot connection issues
├── ConnectionError     — Network/connection failures (retryable flag)
├── MessageError        — Message processing errors
├── ContextError        — Context injection failures
├── ValidationError     — Input validation errors
├── PermissionError     — Permission/authorization errors
└── TimeoutError        — Operation timeout errors
所有自定义错误均继承自
ZhinError
ZhinError (基类)
├── ConfigError         — 配置问题
├── PluginError         — 插件加载/执行失败
├── AdapterError        — 适配器/机器人连接问题
├── ConnectionError     — 网络/连接失败(带有可重试标记)
├── MessageError        — 消息处理错误
├── ContextError        — 上下文注入失败
├── ValidationError     — 输入验证错误
├── PermissionError     — 权限/授权错误
└── TimeoutError        — 操作超时错误

Using Error Classes

使用错误类

ts
import {
  ZhinError, ConfigError, PluginError, AdapterError,
  ConnectionError, MessageError, ContextError,
  ValidationError, PermissionError, TimeoutError,
} from '@zhin.js/core'

// Configuration error
throw new ConfigError('Missing API key', { field: 'apiKey' })

// Plugin error with plugin name
throw new PluginError('Failed to initialize', 'my-plugin', { reason: 'missing dep' })

// Adapter error with adapter and bot name
throw new AdapterError('Connection refused', 'discord', 'bot-1')

// Connection error (retryable)
throw new ConnectionError('Server unreachable', true)

// Permission error
throw new PermissionError('Admin required', 'user-123', 'bot_admin')

// Timeout error
throw new TimeoutError('Operation timed out', 30000)
ts
import {
  ZhinError, ConfigError, PluginError, AdapterError,
  ConnectionError, MessageError, ContextError,
  ValidationError, PermissionError, TimeoutError,
} from '@zhin.js/core'

// 配置错误
throw new ConfigError('缺少API密钥', { field: 'apiKey' })

// 带插件名称的插件错误
throw new PluginError('初始化失败', 'my-plugin', { reason: '缺少依赖' })

// 带适配器和机器人名称的适配器错误
throw new AdapterError('连接被拒绝', 'discord', 'bot-1')

// 可重试的连接错误
throw new ConnectionError('服务器不可达', true)

// 权限错误
throw new PermissionError('需要管理员权限', 'user-123', 'bot_admin')

// 超时错误
throw new TimeoutError('操作超时', 30000)

Error Serialization

错误序列化

ts
try {
  // ...
} catch (err) {
  if (err instanceof ZhinError) {
    console.log(err.toJSON())        // Full JSON representation
    console.log(err.toUserString())  // User-friendly: "[CODE] message"
    console.log(err.code)            // Error code string
    console.log(err.timestamp)       // When the error occurred
    console.log(err.context)         // Additional context data
  }
}
ts
try {
  // ...
} catch (err) {
  if (err instanceof ZhinError) {
    console.log(err.toJSON())        // 完整JSON表示
    console.log(err.toUserString())  // 用户友好格式:"[错误码] 消息"
    console.log(err.code)            // 错误码字符串
    console.log(err.timestamp)       // 错误发生时间
    console.log(err.context)         // 附加上下文数据
  }
}

ErrorManager

ErrorManager

Central error handler registry for organized error handling:
ts
import { errorManager, ErrorHandler } from '@zhin.js/core'

// Register handler for specific error types
errorManager.register('ConnectionError', async (error, context) => {
  console.log('Connection issue:', error.message)
  // Notify admin, log to database, etc.
})

// Register global handler (called for all errors)
errorManager.registerGlobal(async (error, context) => {
  console.log('Error occurred:', error.message)
})

// Handle an error
try {
  await riskyOperation()
} catch (error) {
  await errorManager.handle(error as Error, { source: 'my-plugin' })
}

// Clean up handlers
errorManager.unregister('ConnectionError', myHandler)
errorManager.clear()
集中式错误处理注册中心,用于规范化错误处理:
ts
import { errorManager, ErrorHandler } from '@zhin.js/core'

// 为特定错误类型注册处理程序
errorManager.register('ConnectionError', async (error, context) => {
  console.log('连接问题:', error.message)
  // 通知管理员、记录到数据库等
})

// 注册全局处理程序(处理所有错误)
errorManager.registerGlobal(async (error, context) => {
  console.log('发生错误:', error.message)
})

// 处理错误
try {
  await riskyOperation()
} catch (error) {
  await errorManager.handle(error as Error, { source: 'my-plugin' })
}

// 清理处理程序
errorManager.unregister('ConnectionError', myHandler)
errorManager.clear()

RetryManager

RetryManager

Automatic retry with exponential backoff:
ts
import { RetryManager } from '@zhin.js/core'

const result = await RetryManager.retry(
  async () => {
    // Operation that might fail
    return await fetchExternalAPI()
  },
  {
    maxRetries: 3,              // Maximum retry attempts
    delay: 1000,                // Base delay in ms
    exponentialBackoff: true,   // Double delay each retry
    retryCondition: (error) => {
      // Only retry ConnectionErrors
      return error instanceof ConnectionError && error.retryable
    },
  }
)
支持指数退避的自动重试机制:
ts
import { RetryManager } from '@zhin.js/core'

const result = await RetryManager.retry(
  async () => {
    // 可能失败的操作
    return await fetchExternalAPI()
  },
  {
    maxRetries: 3,              // 最大重试次数
    delay: 1000,                // 基础延迟(毫秒)
    exponentialBackoff: true,   // 每次重试延迟翻倍
    retryCondition: (error) => {
      // 仅对ConnectionError进行重试
      return error instanceof ConnectionError && error.retryable
    },
  }
)

Retry Timing

重试时序

With
exponentialBackoff: true
and
delay: 1000
:
  • Attempt 1 → immediate
  • Attempt 2 → wait 1000ms
  • Attempt 3 → wait 2000ms
  • Attempt 4 → wait 4000ms
exponentialBackoff: true
delay: 1000
时:
  • 第1次尝试 → 立即执行
  • 第2次尝试 → 等待1000毫秒
  • 第3次尝试 → 等待2000毫秒
  • 第4次尝试 → 等待4000毫秒

CircuitBreaker

CircuitBreaker

Prevent cascading failures with the circuit breaker pattern:
ts
import { CircuitBreaker } from '@zhin.js/core'

const breaker = new CircuitBreaker(
  5,      // failureThreshold — open after 5 failures
  60000,  // timeoutMs — stay open for 60s
  10000   // monitoringPeriodMs
)

try {
  const result = await breaker.execute(async () => {
    return await callExternalService()
  })
} catch (error) {
  if (error.message === 'Circuit breaker is OPEN') {
    // Service is down, use fallback
    return fallbackValue
  }
  throw error
}

// Check state
console.log(breaker.getState()) // 'CLOSED' | 'OPEN' | 'HALF_OPEN'

// Manual reset
breaker.reset()
通过断路器模式防止级联故障:
ts
import { CircuitBreaker } from '@zhin.js/core'

const breaker = new CircuitBreaker(
  5,      // failureThreshold — 失败5次后打开断路器
  60000,  // timeoutMs — 保持打开状态60秒
  10000   // monitoringPeriodMs
)

try {
  const result = await breaker.execute(async () => {
    return await callExternalService()
  })
} catch (error) {
  if (error.message === 'Circuit breaker is OPEN') {
    // 服务已下线,使用回退值
    return fallbackValue
  }
  throw error
}

// 检查状态
console.log(breaker.getState()) // 'CLOSED' | 'OPEN' | 'HALF_OPEN'

// 手动重置
breaker.reset()

Circuit Breaker States

断路器状态

StateDescription
CLOSED
Normal operation, requests pass through
OPEN
Too many failures, requests are rejected immediately
HALF_OPEN
Timeout expired, one test request is allowed
状态描述
CLOSED
正常运行状态,请求可正常通过
OPEN
失败次数过多,请求被直接拒绝
HALF_OPEN
超时时间已过,允许发送一次测试请求

Pattern: Plugin Error Handling

模式:插件错误处理

ts
import { usePlugin, PluginError, RetryManager } from 'zhin.js'

const plugin = usePlugin()

plugin.onMounted(async () => {
  try {
    const data = await RetryManager.retry(
      () => loadExternalResource(),
      { maxRetries: 3, delay: 2000 }
    )
    plugin.logger.info('Resource loaded successfully')
  } catch (error) {
    plugin.logger.error(`Failed to load resource: ${error.message}`)
    throw new PluginError('Initialization failed', plugin.name, {
      originalError: error.message,
    })
  }
})
ts
import { usePlugin, PluginError, RetryManager } from 'zhin.js'

const plugin = usePlugin()

plugin.onMounted(async () => {
  try {
    const data = await RetryManager.retry(
      () => loadExternalResource(),
      { maxRetries: 3, delay: 2000 }
    )
    plugin.logger.info('资源加载成功')
  } catch (error) {
    plugin.logger.error(`资源加载失败: ${error.message}`)
    throw new PluginError('初始化失败', plugin.name, {
      originalError: error.message,
    })
  }
})

Checklist

检查清单

  • Use specific error classes instead of generic
    Error
    .
  • Include context information when creating errors.
  • Register error handlers via
    errorManager
    for centralized logging.
  • Use
    RetryManager
    for transient failures (network, API).
  • Use
    CircuitBreaker
    to protect against cascading failures.
  • Check
    ConnectionError.retryable
    before retrying.
  • Clean up error handlers in
    onDispose
    .
  • 使用特定错误类而非通用
    Error
  • 创建错误时包含上下文信息。
  • 通过
    errorManager
    注册错误处理程序以实现集中式日志记录。
  • 对瞬时故障(网络、API)使用
    RetryManager
  • 使用
    CircuitBreaker
    防止级联故障。
  • 重试前检查
    ConnectionError.retryable
    属性。
  • onDispose
    中清理错误处理程序。