route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
409 lines • 16 kB
JavaScript
;
/**
* 统一错误处理系统
* 确保所有错误都正确返回HTTP状态码,不允许静默失败
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnifiedErrorHandler = exports.ValidationError = void 0;
exports.handleProviderError = handleProviderError;
exports.handleStreamingError = handleStreamingError;
exports.handleRoutingError = handleRoutingError;
exports.handleInputError = handleInputError;
exports.handleOutputError = handleOutputError;
const logger_1 = require("@/utils/logger");
const types_1 = require("@/types");
const error_system_diagnostics_1 = require("./error-system-diagnostics");
class ValidationError extends Error {
field;
value;
constraint;
constructor(message, field, value, constraint) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.value = value;
this.constraint = constraint;
}
}
exports.ValidationError = ValidationError;
class UnifiedErrorHandler {
/**
* 处理所有类型的错误,确保返回适当的HTTP状态码
*/
static handleError(error, reply, context) {
// 🔍 Step 1: 诊断错误处理是否正确
const diagnostics = error_system_diagnostics_1.ErrorSystemDiagnostics.diagnoseError(error, reply, {
requestId: context.requestId,
port: process.env.RCC_PORT ? parseInt(process.env.RCC_PORT) : 0, // 🔧 修复硬编码:0表示端口未知
stage: context.stage,
providerId: context.providerId,
isStreaming: context.isStreaming
});
const errorInfo = this.analyzeError(error, context);
// 强制控制台输出错误信息
console.error(`🚨 [ERROR] ${context.stage.toUpperCase()} FAILURE:`);
console.error(` Request ID: ${context.requestId}`);
console.error(` Provider: ${context.providerId || 'unknown'}`);
console.error(` Model: ${context.model || 'unknown'}`);
console.error(` Status: ${errorInfo.statusCode}`);
console.error(` Error: ${errorInfo.message}`);
console.error(` Type: ${errorInfo.type}`);
console.error(` Streaming: ${context.isStreaming ? 'Yes' : 'No'}`);
console.error(` Silent Failure: ${diagnostics.isSilentFailure ? 'YES' : 'NO'}`);
if (context.retryCount !== undefined) {
console.error(` Retries: ${context.retryCount}/${context.maxRetries || 0}`);
}
// 记录详细错误日志
logger_1.logger.error(`${context.stage} failed`, {
requestId: context.requestId,
providerId: context.providerId,
model: context.model,
statusCode: errorInfo.statusCode,
errorType: errorInfo.type,
errorMessage: errorInfo.message,
isStreaming: context.isStreaming,
retryCount: context.retryCount,
maxRetries: context.maxRetries,
stack: error instanceof Error ? error.stack : undefined
}, context.requestId, 'error-handler');
// 对于streaming请求的特殊处理
if (context.isStreaming) {
this.handleStreamingError(error, reply, context, errorInfo);
}
else {
this.handleRegularError(error, reply, context, errorInfo);
}
}
/**
* 处理streaming错误
*/
static handleStreamingError(error, reply, context, errorInfo) {
try {
// 🔧 关键修复:检查HTTP响应是否已经结束,避免"write after end"错误
if (reply.raw.writableEnded || reply.raw.destroyed) {
console.error(`⚠️ [STREAMING] HTTP response already ended, cannot write error response`);
console.error(`🔚 [STREAMING] Connection was already closed for request ${context.requestId}`);
return;
}
// 设置HTTP状态码
reply.code(errorInfo.statusCode);
// 发送简化的错误事件,只包含必要信息
const errorEvent = {
type: 'error',
error: {
type: errorInfo.type,
message: errorInfo.message,
code: errorInfo.statusCode.toString(),
// 只包含基本的provider和model信息
details: {
provider: context.providerId || 'unknown',
model: context.model || 'unknown'
}
}
};
// 🔧 安全写入:检查每个写入步骤
if (!reply.raw.writableEnded) {
reply.raw.write(`event: error\n`);
reply.raw.write(`data: ${JSON.stringify(errorEvent)}\n\n`);
// 确保连接关闭
reply.raw.end();
console.error(`🔚 [STREAMING] Connection closed with status ${errorInfo.statusCode}`);
}
}
catch (writeError) {
console.error(`❌ [STREAMING] Failed to write error response: ${writeError}`);
// 如果无法写入响应,至少确保连接关闭
try {
if (!reply.raw.writableEnded && !reply.raw.destroyed) {
reply.raw.end();
}
}
catch (endError) {
console.error(`❌ [STREAMING] Failed to end connection: ${endError}`);
}
}
}
/**
* 处理常规错误
*/
static handleRegularError(error, reply, context, errorInfo) {
// 根据用户要求,简化错误响应,只包含必要信息
const errorResponse = {
error: {
type: errorInfo.type,
message: errorInfo.message,
code: errorInfo.statusCode.toString(),
// 只包含基本的provider和model信息,移除详细的调试信息
details: {
provider: context.providerId || 'unknown',
model: context.model || 'unknown'
}
}
};
reply.code(errorInfo.statusCode).send(errorResponse);
console.error(`🔚 [REGULAR] Response sent with status ${errorInfo.statusCode} - Provider: ${context.providerId}, Model: ${context.model}`);
}
/**
* 分析错误并确定适当的状态码和类型
*/
static analyzeError(error, context) {
// ProviderError - 保持原始状态码,包含完整的上下文信息
if (error instanceof types_1.ProviderError) {
return {
statusCode: error.statusCode,
type: this.mapStatusCodeToType(error.statusCode),
message: error.message,
details: {
provider: error.provider || context.providerId,
model: context.model,
stage: context.stage,
requestId: context.requestId,
originalError: error.details,
errorCode: error.code,
isStreaming: context.isStreaming,
retryCount: context.retryCount,
maxRetries: context.maxRetries,
timestamp: new Date().toISOString()
}
};
}
// HTTP错误
if (error?.response?.status) {
const status = error.response.status;
return {
statusCode: status,
type: this.mapStatusCodeToType(status),
message: error.message || `HTTP ${status} error`,
details: {
httpStatus: status,
responseData: error.response.data
}
};
}
// 网络错误
if (error?.code === 'ECONNREFUSED' || error?.code === 'ENOTFOUND') {
return {
statusCode: 503,
type: 'service_unavailable',
message: `Network error: ${error.message}`,
details: {
networkError: error.code,
provider: context.providerId
}
};
}
// 超时错误
if (error?.code === 'ETIMEDOUT' || error?.message?.includes('timeout')) {
return {
statusCode: 504,
type: 'gateway_timeout',
message: `Request timeout: ${error.message}`,
details: {
timeoutError: true,
provider: context.providerId
}
};
}
// 认证错误
if (error?.message?.includes('401') || error?.message?.includes('unauthorized')) {
return {
statusCode: 401,
type: 'authentication_error',
message: 'Authentication failed',
details: {
provider: context.providerId,
authError: true
}
};
}
// 权限错误
if (error?.message?.includes('403') || error?.message?.includes('forbidden')) {
return {
statusCode: 403,
type: 'permission_error',
message: 'Permission denied',
details: {
provider: context.providerId,
permissionError: true
}
};
}
// 模型不存在错误
if (error?.message?.includes('404') || error?.message?.includes('not found')) {
return {
statusCode: 404,
type: 'model_not_found',
message: `Model or endpoint not found: ${error.message}`,
details: {
provider: context.providerId,
model: context.model,
stage: context.stage,
requestId: context.requestId,
notFoundError: true,
originalError: error.message,
timestamp: new Date().toISOString()
}
};
}
// 速率限制错误
if (error?.message?.includes('429') || error?.message?.includes('rate limit')) {
return {
statusCode: 429,
type: 'rate_limit_exceeded',
message: 'Rate limit exceeded',
details: {
provider: context.providerId,
rateLimitError: true,
retryCount: context.retryCount
}
};
}
// 多次重试失败
if (context.retryCount !== undefined && context.retryCount >= (context.maxRetries || 0)) {
return {
statusCode: 500,
type: 'max_retries_exceeded',
message: `Request failed after ${context.retryCount} retries: ${error.message}`,
details: {
provider: context.providerId,
model: context.model,
stage: context.stage,
requestId: context.requestId,
retryCount: context.retryCount,
maxRetries: context.maxRetries,
finalError: error.message,
originalError: error instanceof Error ? error.message : String(error),
timestamp: new Date().toISOString()
}
};
}
// 默认内部服务器错误 - 包含完整的调试信息
return {
statusCode: 500,
type: 'internal_server_error',
message: error instanceof Error ? error.message : 'Internal server error',
details: {
provider: context.providerId,
model: context.model,
stage: context.stage,
requestId: context.requestId,
originalError: error instanceof Error ? error.message : String(error),
errorName: error instanceof Error ? error.name : 'UnknownError',
isStreaming: context.isStreaming,
retryCount: context.retryCount,
maxRetries: context.maxRetries,
timestamp: new Date().toISOString(),
stack: error instanceof Error ? error.stack : undefined
}
};
}
/**
* 将HTTP状态码映射到错误类型
*/
static mapStatusCodeToType(statusCode) {
switch (statusCode) {
case 400: return 'bad_request';
case 401: return 'authentication_error';
case 403: return 'permission_error';
case 404: return 'not_found';
case 429: return 'rate_limit_exceeded';
case 500: return 'internal_server_error';
case 502: return 'bad_gateway';
case 503: return 'service_unavailable';
case 504: return 'gateway_timeout';
default: return statusCode >= 500 ? 'server_error' : 'client_error';
}
}
/**
* 检查错误是否应该重试
*/
static shouldRetry(error, currentRetry, maxRetries) {
if (currentRetry >= maxRetries) {
return false;
}
// 不重试的错误类型
const nonRetryableErrors = [400, 401, 403, 404];
const statusCode = error?.response?.status || error?.status;
if (nonRetryableErrors.includes(statusCode)) {
return false;
}
// 可重试的错误类型
const retryableErrors = [429, 500, 502, 503, 504];
if (retryableErrors.includes(statusCode)) {
return true;
}
// 网络错误可重试
if (error?.code === 'ECONNREFUSED' || error?.code === 'ENOTFOUND' || error?.code === 'ETIMEDOUT') {
return true;
}
return false;
}
/**
* 验证错误处理是否正确执行
*/
static validateErrorHandling(error, reply, context) {
// 确保reply已经设置了状态码
if (!reply.statusCode || reply.statusCode === 200) {
console.error(`⚠️ [VALIDATION] Error handler did not set proper status code!`);
console.error(` Context: ${JSON.stringify(context)}`);
console.error(` Error: ${error}`);
// 强制设置500状态码
reply.code(500);
}
// 记录验证信息
logger_1.logger.debug('Error handling validation completed', {
requestId: context.requestId,
statusCode: reply.statusCode,
stage: context.stage,
isStreaming: context.isStreaming
}, context.requestId, 'error-handler');
}
}
exports.UnifiedErrorHandler = UnifiedErrorHandler;
/**
* 便捷函数:处理Provider错误
*/
function handleProviderError(error, reply, context) {
UnifiedErrorHandler.handleError(error, reply, {
...context,
stage: 'provider'
});
}
/**
* 便捷函数:处理Streaming错误
*/
function handleStreamingError(error, reply, context) {
UnifiedErrorHandler.handleError(error, reply, {
...context,
stage: 'streaming',
isStreaming: true
});
}
/**
* 便捷函数:处理路由错误
*/
function handleRoutingError(error, reply, context) {
UnifiedErrorHandler.handleError(error, reply, {
...context,
stage: 'routing'
});
}
/**
* 便捷函数:处理输入处理错误
*/
function handleInputError(error, reply, context) {
UnifiedErrorHandler.handleError(error, reply, {
...context,
stage: 'input-processing'
});
}
/**
* 便捷函数:处理输出处理错误
*/
function handleOutputError(error, reply, context) {
UnifiedErrorHandler.handleError(error, reply, {
...context,
stage: 'output-processing'
});
}
//# sourceMappingURL=error-handler.js.map