From cfe72159d0be564d3e32cd5b87e28f342981dc31 Mon Sep 17 00:00:00 2001 From: erio Date: Sun, 15 Mar 2026 17:25:35 +0800 Subject: [PATCH 1/4] feat(ops): add ignore insufficient balance errors toggle and extract error constants - Add 5th error filter switch IgnoreInsufficientBalanceErrors to suppress upstream insufficient balance / insufficient_quota errors from ops log - Extract hardcoded error strings into package-level constants for shouldSkipOpsErrorLog, normalizeOpsErrorType, classifyOpsPhase, and classifyOpsIsBusinessLimited - Define ErrNoAvailableAccounts sentinel error and replace all errors.New("no available accounts") call sites - Update tests to use require.ErrorIs with the sentinel error --- backend/internal/handler/ops_error_logger.go | 39 +++++++++++++++---- .../service/gateway_multiplatform_test.go | 8 ++-- backend/internal/service/gateway_service.go | 17 ++++---- .../service/openai_account_scheduler.go | 2 +- .../service/openai_gateway_service.go | 6 +-- backend/internal/service/ops_retry.go | 2 +- backend/internal/service/ops_settings.go | 15 +++---- .../internal/service/ops_settings_models.go | 21 +++++----- frontend/src/api/admin/ops.ts | 1 + frontend/src/i18n/locales/en.ts | 2 + frontend/src/i18n/locales/zh.ts | 2 + .../ops/components/OpsSettingsDialog.vue | 10 +++++ 12 files changed, 84 insertions(+), 41 deletions(-) diff --git a/backend/internal/handler/ops_error_logger.go b/backend/internal/handler/ops_error_logger.go index cb2fad5d..5fedc139 100644 --- a/backend/internal/handler/ops_error_logger.go +++ b/backend/internal/handler/ops_error_logger.go @@ -26,6 +26,21 @@ const ( opsStreamKey = "ops_stream" opsRequestBodyKey = "ops_request_body" opsAccountIDKey = "ops_account_id" + + // 错误过滤匹配常量 — shouldSkipOpsErrorLog 和错误分类共用 + opsErrContextCanceled = "context canceled" + opsErrNoAvailableAccounts = "no available accounts" + opsErrInvalidAPIKey = "invalid_api_key" + opsErrAPIKeyRequired = "api_key_required" + opsErrInsufficientBalance = "insufficient balance" + opsErrInsufficientQuota = "insufficient_quota" + + // 上游错误码常量 — 错误分类 (normalizeOpsErrorType / classifyOpsPhase / classifyOpsIsBusinessLimited) + opsCodeInsufficientBalance = "INSUFFICIENT_BALANCE" + opsCodeUsageLimitExceeded = "USAGE_LIMIT_EXCEEDED" + opsCodeSubscriptionNotFound = "SUBSCRIPTION_NOT_FOUND" + opsCodeSubscriptionInvalid = "SUBSCRIPTION_INVALID" + opsCodeUserInactive = "USER_INACTIVE" ) const ( @@ -1024,9 +1039,9 @@ func normalizeOpsErrorType(errType string, code string) string { return errType } switch strings.TrimSpace(code) { - case "INSUFFICIENT_BALANCE": + case opsCodeInsufficientBalance: return "billing_error" - case "USAGE_LIMIT_EXCEEDED", "SUBSCRIPTION_NOT_FOUND", "SUBSCRIPTION_INVALID": + case opsCodeUsageLimitExceeded, opsCodeSubscriptionNotFound, opsCodeSubscriptionInvalid: return "subscription_error" default: return "api_error" @@ -1038,7 +1053,7 @@ func classifyOpsPhase(errType, message, code string) string { // Standardized phases: request|auth|routing|upstream|network|internal // Map billing/concurrency/response => request; scheduling => routing. switch strings.TrimSpace(code) { - case "INSUFFICIENT_BALANCE", "USAGE_LIMIT_EXCEEDED", "SUBSCRIPTION_NOT_FOUND", "SUBSCRIPTION_INVALID": + case opsCodeInsufficientBalance, opsCodeUsageLimitExceeded, opsCodeSubscriptionNotFound, opsCodeSubscriptionInvalid: return "request" } @@ -1057,7 +1072,7 @@ func classifyOpsPhase(errType, message, code string) string { case "upstream_error", "overloaded_error": return "upstream" case "api_error": - if strings.Contains(msg, "no available accounts") { + if strings.Contains(msg, opsErrNoAvailableAccounts) { return "routing" } return "internal" @@ -1103,7 +1118,7 @@ func classifyOpsIsRetryable(errType string, statusCode int) bool { func classifyOpsIsBusinessLimited(errType, phase, code string, status int, message string) bool { switch strings.TrimSpace(code) { - case "INSUFFICIENT_BALANCE", "USAGE_LIMIT_EXCEEDED", "SUBSCRIPTION_NOT_FOUND", "SUBSCRIPTION_INVALID", "USER_INACTIVE": + case opsCodeInsufficientBalance, opsCodeUsageLimitExceeded, opsCodeSubscriptionNotFound, opsCodeSubscriptionInvalid, opsCodeUserInactive: return true } if phase == "billing" || phase == "concurrency" { @@ -1197,21 +1212,29 @@ func shouldSkipOpsErrorLog(ctx context.Context, ops *service.OpsService, message // Check if context canceled errors should be ignored (client disconnects) if settings.IgnoreContextCanceled { - if strings.Contains(msgLower, "context canceled") || strings.Contains(bodyLower, "context canceled") { + if strings.Contains(msgLower, opsErrContextCanceled) || strings.Contains(bodyLower, opsErrContextCanceled) { return true } } // Check if "no available accounts" errors should be ignored if settings.IgnoreNoAvailableAccounts { - if strings.Contains(msgLower, "no available accounts") || strings.Contains(bodyLower, "no available accounts") { + if strings.Contains(msgLower, opsErrNoAvailableAccounts) || strings.Contains(bodyLower, opsErrNoAvailableAccounts) { return true } } // Check if invalid/missing API key errors should be ignored (user misconfiguration) if settings.IgnoreInvalidApiKeyErrors { - if strings.Contains(bodyLower, "invalid_api_key") || strings.Contains(bodyLower, "api_key_required") { + if strings.Contains(bodyLower, opsErrInvalidAPIKey) || strings.Contains(bodyLower, opsErrAPIKeyRequired) { + return true + } + } + + // Check if insufficient balance errors should be ignored + if settings.IgnoreInsufficientBalanceErrors { + if strings.Contains(bodyLower, opsErrInsufficientBalance) || strings.Contains(bodyLower, opsErrInsufficientQuota) || + strings.Contains(msgLower, opsErrInsufficientBalance) { return true } } diff --git a/backend/internal/service/gateway_multiplatform_test.go b/backend/internal/service/gateway_multiplatform_test.go index f947a8ee..ea8fa784 100644 --- a/backend/internal/service/gateway_multiplatform_test.go +++ b/backend/internal/service/gateway_multiplatform_test.go @@ -440,7 +440,7 @@ func TestGatewayService_SelectAccountForModelWithPlatform_NoAvailableAccounts(t acc, err := svc.selectAccountForModelWithPlatform(ctx, nil, "", "claude-3-5-sonnet-20241022", nil, PlatformAnthropic) require.Error(t, err) require.Nil(t, acc) - require.Contains(t, err.Error(), "no available accounts") + require.ErrorIs(t, err, ErrNoAvailableAccounts) } // TestGatewayService_SelectAccountForModelWithPlatform_AllExcluded 测试所有账户被排除 @@ -1073,7 +1073,7 @@ func TestGatewayService_SelectAccountForModelWithPlatform_NoAccounts(t *testing. acc, err := svc.selectAccountForModelWithPlatform(ctx, nil, "", "", nil, PlatformAnthropic) require.Error(t, err) require.Nil(t, acc) - require.Contains(t, err.Error(), "no available accounts") + require.ErrorIs(t, err, ErrNoAvailableAccounts) } func TestGatewayService_isModelSupportedByAccount(t *testing.T) { @@ -1734,7 +1734,7 @@ func TestGatewayService_selectAccountWithMixedScheduling(t *testing.T) { acc, err := svc.selectAccountWithMixedScheduling(ctx, nil, "", "claude-3-5-sonnet-20241022", nil, PlatformAnthropic) require.Error(t, err) require.Nil(t, acc) - require.Contains(t, err.Error(), "no available accounts") + require.ErrorIs(t, err, ErrNoAvailableAccounts) }) t.Run("混合调度-不支持模型返回错误", func(t *testing.T) { @@ -2290,7 +2290,7 @@ func TestGatewayService_SelectAccountWithLoadAwareness(t *testing.T) { result, err := svc.SelectAccountWithLoadAwareness(ctx, nil, "", "claude-3-5-sonnet-20241022", nil, "") require.Error(t, err) require.Nil(t, result) - require.Contains(t, err.Error(), "no available accounts") + require.ErrorIs(t, err, ErrNoAvailableAccounts) }) t.Run("过滤不可调度账号-限流账号被跳过", func(t *testing.T) { diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go index 70f64121..cff9e9bb 100644 --- a/backend/internal/service/gateway_service.go +++ b/backend/internal/service/gateway_service.go @@ -346,6 +346,9 @@ var systemBlockFilterPrefixes = []string{ "x-anthropic-billing-header", } +// ErrNoAvailableAccounts 表示没有可用的账号 +var ErrNoAvailableAccounts = errors.New("no available accounts") + // ErrClaudeCodeOnly 表示分组仅允许 Claude Code 客户端访问 var ErrClaudeCodeOnly = errors.New("this group only allows Claude Code clients") @@ -1205,7 +1208,7 @@ func (s *GatewayService) SelectAccountWithLoadAwareness(ctx context.Context, gro return nil, err } if len(accounts) == 0 { - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } ctx = s.withWindowCostPrefetch(ctx, accounts) ctx = s.withRPMPrefetch(ctx, accounts) @@ -1553,7 +1556,7 @@ func (s *GatewayService) SelectAccountWithLoadAwareness(ctx context.Context, gro } if len(candidates) == 0 { - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } accountLoads := make([]AccountWithConcurrency, 0, len(candidates)) @@ -1642,7 +1645,7 @@ func (s *GatewayService) SelectAccountWithLoadAwareness(ctx context.Context, gro }, }, nil } - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } func (s *GatewayService) tryAcquireByLegacyOrder(ctx context.Context, candidates []*Account, groupID *int64, sessionHash string, preferOAuth bool) (*AccountSelectionResult, bool) { @@ -2852,9 +2855,9 @@ func (s *GatewayService) selectAccountForModelWithPlatform(ctx context.Context, if selected == nil { stats := s.logDetailedSelectionFailure(ctx, groupID, sessionHash, requestedModel, platform, accounts, excludedIDs, false) if requestedModel != "" { - return nil, fmt.Errorf("no available accounts supporting model: %s (%s)", requestedModel, summarizeSelectionFailureStats(stats)) + return nil, fmt.Errorf("%w supporting model: %s (%s)", ErrNoAvailableAccounts, requestedModel, summarizeSelectionFailureStats(stats)) } - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } // 4. 建立粘性绑定 @@ -3090,9 +3093,9 @@ func (s *GatewayService) selectAccountWithMixedScheduling(ctx context.Context, g if selected == nil { stats := s.logDetailedSelectionFailure(ctx, groupID, sessionHash, requestedModel, nativePlatform, accounts, excludedIDs, true) if requestedModel != "" { - return nil, fmt.Errorf("no available accounts supporting model: %s (%s)", requestedModel, summarizeSelectionFailureStats(stats)) + return nil, fmt.Errorf("%w supporting model: %s (%s)", ErrNoAvailableAccounts, requestedModel, summarizeSelectionFailureStats(stats)) } - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } // 4. 建立粘性绑定 diff --git a/backend/internal/service/openai_account_scheduler.go b/backend/internal/service/openai_account_scheduler.go index 0fcf450b..789888cb 100644 --- a/backend/internal/service/openai_account_scheduler.go +++ b/backend/internal/service/openai_account_scheduler.go @@ -725,7 +725,7 @@ func (s *defaultOpenAIAccountScheduler) selectByLoadBalance( }, len(candidates), topK, loadSkew, nil } - return nil, len(candidates), topK, loadSkew, errors.New("no available accounts") + return nil, len(candidates), topK, loadSkew, ErrNoAvailableAccounts } func (s *defaultOpenAIAccountScheduler) isAccountTransportCompatible(account *Account, requiredTransport OpenAIUpstreamTransport) bool { diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index 406344e3..327ce916 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -1312,7 +1312,7 @@ func (s *OpenAIGatewayService) SelectAccountWithLoadAwareness(ctx context.Contex return nil, err } if len(accounts) == 0 { - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } isExcluded := func(accountID int64) bool { @@ -1382,7 +1382,7 @@ func (s *OpenAIGatewayService) SelectAccountWithLoadAwareness(ctx context.Contex } if len(candidates) == 0 { - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } accountLoads := make([]AccountWithConcurrency, 0, len(candidates)) @@ -1489,7 +1489,7 @@ func (s *OpenAIGatewayService) SelectAccountWithLoadAwareness(ctx context.Contex }, nil } - return nil, errors.New("no available accounts") + return nil, ErrNoAvailableAccounts } func (s *OpenAIGatewayService) listSchedulableAccounts(ctx context.Context, groupID *int64) ([]Account, error) { diff --git a/backend/internal/service/ops_retry.go b/backend/internal/service/ops_retry.go index f0daa3e2..fdabbafd 100644 --- a/backend/internal/service/ops_retry.go +++ b/backend/internal/service/ops_retry.go @@ -467,7 +467,7 @@ func (s *OpsService) executeClientRetry(ctx context.Context, reqType opsRetryReq return &opsRetryExecution{status: opsRetryStatusFailed, errorMessage: selErr.Error()} } if selection == nil || selection.Account == nil { - return &opsRetryExecution{status: opsRetryStatusFailed, errorMessage: "no available accounts"} + return &opsRetryExecution{status: opsRetryStatusFailed, errorMessage: ErrNoAvailableAccounts.Error()} } account := selection.Account diff --git a/backend/internal/service/ops_settings.go b/backend/internal/service/ops_settings.go index 93815887..5871166c 100644 --- a/backend/internal/service/ops_settings.go +++ b/backend/internal/service/ops_settings.go @@ -368,13 +368,14 @@ func defaultOpsAdvancedSettings() *OpsAdvancedSettings { Aggregation: OpsAggregationSettings{ AggregationEnabled: false, }, - IgnoreCountTokensErrors: true, // count_tokens 404 是预期行为,默认忽略 - IgnoreContextCanceled: true, // Default to true - client disconnects are not errors - IgnoreNoAvailableAccounts: false, // Default to false - this is a real routing issue - DisplayOpenAITokenStats: false, - DisplayAlertEvents: true, - AutoRefreshEnabled: false, - AutoRefreshIntervalSec: 30, + IgnoreCountTokensErrors: true, // count_tokens 404 是预期行为,默认忽略 + IgnoreContextCanceled: true, // Default to true - client disconnects are not errors + IgnoreNoAvailableAccounts: false, // Default to false - this is a real routing issue + IgnoreInsufficientBalanceErrors: false, // 默认不忽略,余额不足可能需要关注 + DisplayOpenAITokenStats: false, + DisplayAlertEvents: true, + AutoRefreshEnabled: false, + AutoRefreshIntervalSec: 30, } } diff --git a/backend/internal/service/ops_settings_models.go b/backend/internal/service/ops_settings_models.go index c8b9fcd1..fa18b05f 100644 --- a/backend/internal/service/ops_settings_models.go +++ b/backend/internal/service/ops_settings_models.go @@ -92,16 +92,17 @@ type OpsAlertRuntimeSettings struct { // OpsAdvancedSettings stores advanced ops configuration (data retention, aggregation). type OpsAdvancedSettings struct { - DataRetention OpsDataRetentionSettings `json:"data_retention"` - Aggregation OpsAggregationSettings `json:"aggregation"` - IgnoreCountTokensErrors bool `json:"ignore_count_tokens_errors"` - IgnoreContextCanceled bool `json:"ignore_context_canceled"` - IgnoreNoAvailableAccounts bool `json:"ignore_no_available_accounts"` - IgnoreInvalidApiKeyErrors bool `json:"ignore_invalid_api_key_errors"` - DisplayOpenAITokenStats bool `json:"display_openai_token_stats"` - DisplayAlertEvents bool `json:"display_alert_events"` - AutoRefreshEnabled bool `json:"auto_refresh_enabled"` - AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"` + DataRetention OpsDataRetentionSettings `json:"data_retention"` + Aggregation OpsAggregationSettings `json:"aggregation"` + IgnoreCountTokensErrors bool `json:"ignore_count_tokens_errors"` + IgnoreContextCanceled bool `json:"ignore_context_canceled"` + IgnoreNoAvailableAccounts bool `json:"ignore_no_available_accounts"` + IgnoreInvalidApiKeyErrors bool `json:"ignore_invalid_api_key_errors"` + IgnoreInsufficientBalanceErrors bool `json:"ignore_insufficient_balance_errors"` + DisplayOpenAITokenStats bool `json:"display_openai_token_stats"` + DisplayAlertEvents bool `json:"display_alert_events"` + AutoRefreshEnabled bool `json:"auto_refresh_enabled"` + AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"` } type OpsDataRetentionSettings struct { diff --git a/frontend/src/api/admin/ops.ts b/frontend/src/api/admin/ops.ts index 11699c79..64f6a6d0 100644 --- a/frontend/src/api/admin/ops.ts +++ b/frontend/src/api/admin/ops.ts @@ -841,6 +841,7 @@ export interface OpsAdvancedSettings { ignore_context_canceled: boolean ignore_no_available_accounts: boolean ignore_invalid_api_key_errors: boolean + ignore_insufficient_balance_errors: boolean display_openai_token_stats: boolean display_alert_events: boolean auto_refresh_enabled: boolean diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 7f96f63c..a41bc89a 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -3842,6 +3842,8 @@ export default { ignoreNoAvailableAccountsHint: 'When enabled, "No available accounts" errors will not be written to the error log (not recommended; usually a config issue).', ignoreInvalidApiKeyErrors: 'Ignore invalid API key errors', ignoreInvalidApiKeyErrorsHint: 'When enabled, invalid or missing API key errors (INVALID_API_KEY, API_KEY_REQUIRED) will not be written to the error log.', + ignoreInsufficientBalanceErrors: 'Ignore Insufficient Balance Errors', + ignoreInsufficientBalanceErrorsHint: 'When enabled, upstream insufficient account balance errors will not be written to the error log.', autoRefresh: 'Auto Refresh', enableAutoRefresh: 'Enable auto refresh', enableAutoRefreshHint: 'Automatically refresh dashboard data at a fixed interval.', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index c25ff211..4bf7a0b3 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -4016,6 +4016,8 @@ export default { ignoreNoAvailableAccountsHint: '启用后,"No available accounts" 错误将不会写入错误日志(不推荐,这通常是配置问题)。', ignoreInvalidApiKeyErrors: '忽略无效 API Key 错误', ignoreInvalidApiKeyErrorsHint: '启用后,无效或缺失 API Key 的错误(INVALID_API_KEY、API_KEY_REQUIRED)将不会写入错误日志。', + ignoreInsufficientBalanceErrors: '忽略余额不足错误', + ignoreInsufficientBalanceErrorsHint: '启用后,上游账号余额不足(Insufficient balance)的错误将不会写入错误日志。', autoRefresh: '自动刷新', enableAutoRefresh: '启用自动刷新', enableAutoRefreshHint: '自动刷新仪表板数据,启用后会定期拉取最新数据。', diff --git a/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue b/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue index 5dcd5c62..542f111d 100644 --- a/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue +++ b/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue @@ -516,6 +516,16 @@ async function saveAllSettings() { + +
+
+ +

+ {{ t('admin.ops.settings.ignoreInsufficientBalanceErrorsHint') }} +

+
+ +
From 6177fa5dd8715b2cf7edadc210b5492d138f9f8d Mon Sep 17 00:00:00 2001 From: erio Date: Sun, 15 Mar 2026 17:41:40 +0800 Subject: [PATCH 2/4] fix(i18n): correct insufficient balance error hint text Remove misleading "upstream" wording - the error is about client API key user balance, not upstream account balance. --- frontend/src/i18n/locales/en.ts | 2 +- frontend/src/i18n/locales/zh.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index a41bc89a..e0ee6cc4 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -3843,7 +3843,7 @@ export default { ignoreInvalidApiKeyErrors: 'Ignore invalid API key errors', ignoreInvalidApiKeyErrorsHint: 'When enabled, invalid or missing API key errors (INVALID_API_KEY, API_KEY_REQUIRED) will not be written to the error log.', ignoreInsufficientBalanceErrors: 'Ignore Insufficient Balance Errors', - ignoreInsufficientBalanceErrorsHint: 'When enabled, upstream insufficient account balance errors will not be written to the error log.', + ignoreInsufficientBalanceErrorsHint: 'When enabled, insufficient account balance errors will not be written to the error log.', autoRefresh: 'Auto Refresh', enableAutoRefresh: 'Enable auto refresh', enableAutoRefreshHint: 'Automatically refresh dashboard data at a fixed interval.', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 4bf7a0b3..431dcccb 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -4017,7 +4017,7 @@ export default { ignoreInvalidApiKeyErrors: '忽略无效 API Key 错误', ignoreInvalidApiKeyErrorsHint: '启用后,无效或缺失 API Key 的错误(INVALID_API_KEY、API_KEY_REQUIRED)将不会写入错误日志。', ignoreInsufficientBalanceErrors: '忽略余额不足错误', - ignoreInsufficientBalanceErrorsHint: '启用后,上游账号余额不足(Insufficient balance)的错误将不会写入错误日志。', + ignoreInsufficientBalanceErrorsHint: '启用后,账号余额不足(Insufficient balance)的错误将不会写入错误日志。', autoRefresh: '自动刷新', enableAutoRefresh: '启用自动刷新', enableAutoRefreshHint: '自动刷新仪表板数据,启用后会定期拉取最新数据。', From 63f3af0f947986d2387b58c83bee7bc22260901c Mon Sep 17 00:00:00 2001 From: erio Date: Sun, 15 Mar 2026 18:44:13 +0800 Subject: [PATCH 3/4] fix(ops): match "insufficient account balance" in error filter The upstream Gemini API returns "Insufficient account balance" which doesn't contain the substring "insufficient balance". Add explicit match for the full phrase to ensure the filter works correctly. --- backend/internal/handler/ops_error_logger.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/internal/handler/ops_error_logger.go b/backend/internal/handler/ops_error_logger.go index 5fedc139..040276c1 100644 --- a/backend/internal/handler/ops_error_logger.go +++ b/backend/internal/handler/ops_error_logger.go @@ -32,8 +32,9 @@ const ( opsErrNoAvailableAccounts = "no available accounts" opsErrInvalidAPIKey = "invalid_api_key" opsErrAPIKeyRequired = "api_key_required" - opsErrInsufficientBalance = "insufficient balance" - opsErrInsufficientQuota = "insufficient_quota" + opsErrInsufficientBalance = "insufficient balance" + opsErrInsufficientAccountBalance = "insufficient account balance" + opsErrInsufficientQuota = "insufficient_quota" // 上游错误码常量 — 错误分类 (normalizeOpsErrorType / classifyOpsPhase / classifyOpsIsBusinessLimited) opsCodeInsufficientBalance = "INSUFFICIENT_BALANCE" @@ -1233,8 +1234,9 @@ func shouldSkipOpsErrorLog(ctx context.Context, ops *service.OpsService, message // Check if insufficient balance errors should be ignored if settings.IgnoreInsufficientBalanceErrors { - if strings.Contains(bodyLower, opsErrInsufficientBalance) || strings.Contains(bodyLower, opsErrInsufficientQuota) || - strings.Contains(msgLower, opsErrInsufficientBalance) { + if strings.Contains(bodyLower, opsErrInsufficientBalance) || strings.Contains(bodyLower, opsErrInsufficientAccountBalance) || + strings.Contains(bodyLower, opsErrInsufficientQuota) || + strings.Contains(msgLower, opsErrInsufficientBalance) || strings.Contains(msgLower, opsErrInsufficientAccountBalance) { return true } } From bdbc8fa08f8dfa257ce73edb9b65a7cb25fcf26e Mon Sep 17 00:00:00 2001 From: erio Date: Sun, 15 Mar 2026 18:55:14 +0800 Subject: [PATCH 4/4] fix(ops): align constant declarations for gofmt compliance --- backend/internal/handler/ops_error_logger.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/internal/handler/ops_error_logger.go b/backend/internal/handler/ops_error_logger.go index 040276c1..ceb06f0e 100644 --- a/backend/internal/handler/ops_error_logger.go +++ b/backend/internal/handler/ops_error_logger.go @@ -28,10 +28,10 @@ const ( opsAccountIDKey = "ops_account_id" // 错误过滤匹配常量 — shouldSkipOpsErrorLog 和错误分类共用 - opsErrContextCanceled = "context canceled" - opsErrNoAvailableAccounts = "no available accounts" - opsErrInvalidAPIKey = "invalid_api_key" - opsErrAPIKeyRequired = "api_key_required" + opsErrContextCanceled = "context canceled" + opsErrNoAvailableAccounts = "no available accounts" + opsErrInvalidAPIKey = "invalid_api_key" + opsErrAPIKeyRequired = "api_key_required" opsErrInsufficientBalance = "insufficient balance" opsErrInsufficientAccountBalance = "insufficient account balance" opsErrInsufficientQuota = "insufficient_quota"