From 9b211b063b37bcdbc430def00fefadda2d521a54 Mon Sep 17 00:00:00 2001 From: shaw Date: Sat, 11 Oct 2025 13:00:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8Dclaude=E7=9A=84400?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/claudeRelayService.js | 81 ++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/services/claudeRelayService.js b/src/services/claudeRelayService.js index 8d8909b8..549c595e 100644 --- a/src/services/claudeRelayService.js +++ b/src/services/claudeRelayService.js @@ -38,6 +38,57 @@ class ClaudeRelayService { return `此专属账号的Opus模型已达到周使用限制,将于 ${formattedReset} 自动恢复,请尝试切换其他模型后再试。` } + // 🧾 提取错误消息文本 + _extractErrorMessage(body) { + if (!body) { + return '' + } + + if (typeof body === 'string') { + const trimmed = body.trim() + if (!trimmed) { + return '' + } + try { + const parsed = JSON.parse(trimmed) + return this._extractErrorMessage(parsed) + } catch (error) { + return trimmed + } + } + + if (typeof body === 'object') { + if (typeof body.error === 'string') { + return body.error + } + if (body.error && typeof body.error === 'object') { + if (typeof body.error.message === 'string') { + return body.error.message + } + if (typeof body.error.error === 'string') { + return body.error.error + } + } + if (typeof body.message === 'string') { + return body.message + } + } + + return '' + } + + // 🚫 检查是否为组织被禁用错误 + _isOrganizationDisabledError(statusCode, body) { + if (statusCode !== 400) { + return false + } + const message = this._extractErrorMessage(body) + if (!message) { + return false + } + return message.toLowerCase().includes('this organization has been disabled') + } + // 🔍 判断是否是真实的 Claude Code 请求 isRealClaudeCodeRequest(requestBody) { return ClaudeCodeValidator.hasClaudeCodeSystemPrompt(requestBody) @@ -189,6 +240,10 @@ class ClaudeRelayService { let isRateLimited = false let rateLimitResetTimestamp = null let dedicatedRateLimitMessage = null + const organizationDisabledError = this._isOrganizationDisabledError( + response.statusCode, + response.body + ) // 检查是否为401状态码(未授权) if (response.statusCode === 401) { @@ -221,6 +276,13 @@ class ClaudeRelayService { ) await unifiedClaudeScheduler.markAccountBlocked(accountId, accountType, sessionHash) } + // 检查是否返回组织被禁用错误(400状态码) + else if (organizationDisabledError) { + logger.error( + `🚫 Organization disabled error (400) detected for account ${accountId}, marking as blocked` + ) + await unifiedClaudeScheduler.markAccountBlocked(accountId, accountType, sessionHash) + } // 检查是否为529状态码(服务过载) else if (response.statusCode === 529) { logger.warn(`🚫 Overload error (529) detected for account ${accountId}`) @@ -1253,6 +1315,25 @@ class ClaudeRelayService { `❌ Claude API error response (Account: ${account?.name || accountId}):`, errorData ) + if (this._isOrganizationDisabledError(res.statusCode, errorData)) { + ;(async () => { + try { + logger.error( + `🚫 [Stream] Organization disabled error (400) detected for account ${accountId}, marking as blocked` + ) + await unifiedClaudeScheduler.markAccountBlocked( + accountId, + accountType, + sessionHash + ) + } catch (markError) { + logger.error( + `❌ [Stream] Failed to mark account ${accountId} as blocked after organization disabled error:`, + markError + ) + } + })() + } if (!responseStream.destroyed) { // 发送错误事件 responseStream.write('event: error\n')