Skip to content

fix(logs): improve fake 200 error logs#790

Merged
ding113 merged 4 commits intodevfrom
refactor/fake-200-error-logs
Feb 15, 2026
Merged

fix(logs): improve fake 200 error logs#790
ding113 merged 4 commits intodevfrom
refactor/fake-200-error-logs

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Feb 15, 2026

Summary

This PR significantly improves fake 200 error detection and logging throughout the proxy system. Upstream providers sometimes return HTTP 200 with error content (HTML error pages, JSON error objects, or empty bodies), which previously bypassed circuit breakers and caused incorrect session binding.

Problem

When upstream providers return HTTP 200 with error content (Cloudflare/WAF challenge pages, authentication failures in JSON, empty responses), the proxy incorrectly recorded these as successes. This caused:

  • Circuit breaker/failover statistics to record false successes
  • Session smart binding to update to unavailable providers
  • Subsequent retries continuing to hit the same bad provider

Related Issues:

Solution

Core Detection Logic:

  • New upstream-error-detection.ts utility detects fake 200s via HTML document sniffing (DOCTYPE/html tags), JSON error field inspection, and empty body checks
  • Non-streaming responses: inspects response bodies up to 32KB for HTML/JSON content types
  • Streaming responses: performs deferred finalization after SSE streams end

Status Code Inference:

  • Matches error content against 14+ patterns (rate limit → 429, unauthorized → 401, forbidden → 403, payment → 402, not found → 404, etc.)
  • Infers semantic 4xx/5xx codes from response body text for internal circuit breaker/failover decisions

Security:

  • Automatically redacts Bearer tokens, API keys, JWTs, emails, and sensitive key-value pairs from error details before logging
  • Raw body snippets only exposed when verboseProviderError system setting is enabled

Changes

Core Changes

  • src/lib/utils/upstream-error-detection.ts - Adds fake 200 detection and status code inference
  • src/app/v1/_lib/proxy/forwarder.ts - Non-streaming fake 200 detection with Content-Type inspection
  • src/app/v1/_lib/proxy/response-handler.ts - Streaming SSE fake 200 detection during finalization
  • src/app/v1/_lib/proxy/error-handler.ts - Verbose error details with sanitization
  • src/app/v1/_lib/proxy/errors.ts - Extended ProxyError with statusCodeInferred fields

UI Changes

  • src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx - Displays inferred status badges
  • Added complete i18n for all 5 languages (en, ja, ru, zh-CN, zh-TW)

Tests

  • tests/unit/proxy/proxy-forwarder-fake-200-html.test.ts - 536-line test suite for HTML detection
  • src/lib/utils/upstream-error-detection.test.ts - Status inference pattern tests

Breaking Changes

None. This is a purely additive detection enhancement.

Testing

Automated Tests

  • Unit tests for HTML document detection (DOCTYPE, BOM handling)
  • Unit tests for status code inference patterns (429, 401, 403, 402, 404, etc.)
  • Integration tests for fake 200 failover behavior

Manual Testing

  1. Configure a provider that returns 200 + HTML (e.g., behind Cloudflare challenge)
  2. Send a request through the proxy
  3. Verify the response triggers failover to the next provider
  4. Confirm "Inferred" badge appears in logs UI for inferred status codes

Description enhanced by Claude AI

Greptile Summary

This PR implements comprehensive fake-200 error detection throughout the proxy system, addressing cases where upstream providers return HTTP 200 with error content (HTML pages, JSON errors, or empty bodies). The implementation properly distinguishes between streaming and non-streaming responses, with deferred finalization for SSE and immediate detection for non-streaming requests.

Key improvements:

  • New upstream-error-detection.ts utility with HTML document sniffing, JSON error field inspection, and semantic status code inference (14+ patterns covering 429, 401, 403, 402, 404, etc.)
  • Non-streaming detection in forwarder.ts inspects bodies up to 32KB with Content-Type awareness
  • Streaming detection in response-handler.ts performs deferred checks after SSE completion
  • Security: automatic redaction of Bearer tokens, API keys, JWTs, emails before logging
  • UI displays "Inferred" badges for inferred status codes with complete i18n (en, ja, ru, zh-CN, zh-TW)
  • 404 errors correctly skip circuit breaker recording (resource not found vs provider fault)

Test coverage:

  • 573-line HTML detection test suite
  • Comprehensive unit tests for status inference, sanitization, BOM handling
  • Total 681 lines of new test code

Issues found:

  • Critical: forwarder.ts line 834-839 has logic bug where valid responses with non-HTML/non-JSON content types and missing Content-Length incorrectly throw empty response errors
  • Style: Some regex patterns noted in previous threads as potentially over-broad (already documented)

The architecture is sound with proper separation of concerns, thorough error handling, and extensive test coverage. The one logic bug should be fixed before merge.

Confidence Score: 4/5

  • Safe to merge with one logic issue to address - comprehensive fake-200 detection with strong test coverage
  • Score reflects thorough implementation with extensive tests (681 test lines), complete i18n across 5 languages, and well-structured detection logic. One critical logic bug in forwarder.ts line 834-839 needs fixing where missing body inspection can incorrectly throw empty response errors for valid responses. Previous regex over-matching concerns in upstream-error-detection.ts were already noted. Security sanitization is properly implemented.
  • src/app/v1/_lib/proxy/forwarder.ts (fix empty response logic bug at line 834-839)

Important Files Changed

Filename Overview
src/lib/utils/upstream-error-detection.ts Adds comprehensive fake-200 detection with HTML sniffing, JSON error field checks, status inference patterns, and sanitization - previous review comments about overly broad regexes noted
src/app/v1/_lib/proxy/forwarder.ts Implements non-streaming fake-200 detection with Content-Type inspection, body reading up to 32KB, and Content-Length validation - substantial logic additions
src/app/v1/_lib/proxy/response-handler.ts Adds deferred streaming finalization with status inference for fake-200 SSE responses, includes 404 special handling to skip circuit breaker
src/app/v1/_lib/proxy/errors.ts Extends ProxyError with statusCodeInferred and statusCodeInferenceMatcherId fields for fake-200 tracking
tests/unit/proxy/proxy-forwarder-fake-200-html.test.ts Comprehensive 573-line test suite for HTML fake-200 detection covering DOCTYPE, BOM, Content-Type scenarios
src/lib/utils/upstream-error-detection.test.ts Thorough test coverage for detection logic including sanitization, BOM handling, SSE parsing, and status inference patterns

Flowchart

flowchart TD
    A[Upstream Response<br/>HTTP 200] --> B{Content-Type?}
    
    B -->|text/event-stream| C[SSE Stream]
    B -->|text/html or<br/>application/json| D[Non-Stream]
    B -->|Other| D
    
    C --> C1[Defer Finalization<br/>to ResponseHandler]
    C1 --> C2[Stream Ends]
    C2 --> C3[Parse SSE Events]
    C3 --> C4{Detect Error?}
    C4 -->|error field<br/>non-empty| E1[Fake 200 Detected]
    C4 -->|message keyword<br/>match| E1
    C4 -->|Empty body| E1
    C4 -->|No error| C5[Success]
    
    D --> D1{Content-Length?}
    D1 -->|0| E2[Empty Response Error]
    D1 -->|Valid & > 0| D2{Inspect Body?}
    D1 -->|Missing/Invalid| D3[Clone & Read Body]
    
    D2 -->|HTML Content-Type| D3
    D2 -->|JSON < 32KB| D3
    D2 -->|Other| D4[Skip Inspection]
    
    D3 --> D5[Read up to 32KB]
    D5 --> D6{Detect Error?}
    D6 -->|HTML Document<br/>DOCTYPE/html tag| E1
    D6 -->|JSON error field<br/>non-empty| E1
    D6 -->|Empty body| E2
    D6 -->|No strong signal| D7[Parse JSON Content]
    
    D4 --> D7
    D7 --> D8{Valid Output?}
    D8 -->|Yes| C5
    D8 -->|No content| E2
    
    E1 --> E3[Infer Status Code]
    E3 --> E4{Match Pattern?}
    E4 -->|rate_limit| F1[429]
    E4 -->|unauthorized| F2[401]
    E4 -->|forbidden| F3[403]
    E4 -->|payment_required| F4[402]
    E4 -->|not_found| F5[404]
    E4 -->|No match| F6[502 default]
    
    F1 & F2 & F3 & F4 & F5 & F6 --> G[Trigger Failover]
    G --> H{404?}
    H -->|Yes| I[Skip Circuit Breaker]
    H -->|No| J[Record Circuit Breaker]
    
    E2 --> G
    
    I & J --> K[Update Provider Chain]
    K --> L[Retry Next Provider]
    
    C5 --> M[Record Success]
    M --> N[Update Session Binding]
    N --> O[Return Response]
Loading

Last reviewed commit: f0b99a4

@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

📝 Walkthrough

Walkthrough

本次变更:在代理转发与响应处理链增加“假 200”/上游错误文本嗅探与状态码推断,将推断元数据贯穿 provider-chain 与统计,后端在启用冗长模式时返回脱敏上游详情;前端添加相应 i18n 文案与 UI(推断徽章、原因展示);并补充大量单元测试覆盖这些路径。

Changes

Cohort / File(s) Summary
多语言本地化 - 仪表盘
messages/en/dashboard.json, messages/ja/dashboard.json, messages/ru/dashboard.json, messages/zh-CN/dashboard.json, messages/zh-TW/dashboard.json
新增 FAKE_200 原因映射与“推断”状态码显示相关翻译键(fake200DetectedReasonfake200ReasonsstatusCodeInferredBadgestatusCodeInferredTooltipstatusCodeInferredSuffix),以及 logicTrace 中的 httpStatus 格式化键。
多语言本地化 - 提供商链
messages/en/provider-chain.json, messages/ja/provider-chain.json, messages/ru/provider-chain.json, messages/zh-CN/provider-chain.json, messages/zh-TW/provider-chain.json
新增 resource_not_found/404 及推断状态码相关键(resourceNotFoundresource_not_foundresourceNotFoundFailedstatusCodeInferredresourceNotFoundNote)。
前端:错误详情与提供商链 UI
src/app/[locale]/dashboard/logs/_components/.../SummaryTab.tsx, .../LogicTraceTab.tsx, .../provider-chain-popover.tsx, src/app/[locale]/dashboard/logs/_components/fake200-reason.ts, tests: .../error-details-dialog.test.tsx, .../provider-chain-popover.test.tsx
新增 UI 辅助映射及显示:将 FAKE_200 原因映射为 i18n 键并在 Summary/Popover/LogicTrace 中展示推断徽章与原因;将 resource_not_found 视为失败类别;相应测试扩展以断言显示与布局。
后端:转发器与非流响应检测
src/app/v1/_lib/proxy/forwarder.ts, src/app/v1/_lib/proxy/response-handler.ts
对非流式 2xx 响应添加截取读取与内容嗅探(HTML/JSON/error heuristics),调用文本推断以生成 statusCodeInferred 与 matcherId,按推断决定失败类型(含 resource_not_found)并将元数据传入日志/chain/stat。
后端:错误类型与错误处理
src/app/v1/_lib/proxy/errors.ts, src/app/v1/_lib/proxy/error-handler.ts
扩展 upstreamError 元数据(rawBodyrawBodyTruncatedstatusCodeInferredstatusCodeInferenceMatcherId);getClientSafeMessage 为 FAKE_200 场景生成可读原因并可附加经脱敏的上游片段;error-handler 在启用 verboseProviderError 时构建并返回脱敏上游详情(含 safeRequestId)。
会话、格式化与类型
src/app/v1/_lib/proxy/session.ts, src/types/message.ts, src/lib/utils/provider-chain-formatter.ts, src/lib/utils/provider-chain-formatter.test.ts
类型:ProviderChainItem 加入可选 statusCodeInferred;addProviderToChain 可接收并传播该元数据;格式化器统一使用 formatTimelineStatusCode 并添加 resource_not_found 分支及对应测试。
上游错误检测工具与测试
src/lib/utils/upstream-error-detection.ts, src/lib/utils/upstream-error-detection.test.ts
新增上游状态码推断 API inferUpstreamErrorStatusCodeFromText、HTML 文档嗅探、错误匹配器集合与 sanitizeErrorTextForDetail,并补充大量针对推断规则与边界情况的测试。
单元测试扩展与行为验证
tests/unit/proxy/error-handler-verbose-provider-error-details.test.ts, tests/unit/proxy/proxy-forwarder-fake-200-html.test.ts, tests/unit/proxy/proxy-forwarder-endpoint-audit.test.ts, tests/unit/proxy/response-handler-endpoint-circuit-isolation.test.ts, src/lib/utils/upstream-error-detection.test.ts, src/lib/utils/provider-chain-formatter.test.ts
新增/扩展大量单元测试,覆盖 fake-200(HTML/JSON)、状态码推断(429/401/403/404/413 等)、EmptyResponse、冗长错误详情与脱敏规则、endpoint_pool_exhausted 与 provider-chain 顺序与内容不变性测试。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'fix(logs): improve fake 200 error logs' accurately describes the main objective of detecting and improving logging for HTTP 200 responses with error content, which is the core focus of this changeset.
Linked Issues check ✅ Passed The PR fully addresses the core requirement from issue #749: detecting HTTP 200 responses with error content (HTML, JSON errors, empty bodies), treating them as failures, inferring status codes, and improving observability in the dashboard. The implementation includes detection logic, status inference patterns, sanitization, and UI display of inferred status badges and fake-200 reasons.
Out of Scope Changes check ✅ Passed All changes are within scope. The PR focuses exclusively on fake-200 detection, status code inference, error handling, sanitization, UI improvements, and comprehensive testing. There are no unrelated modifications to other features or refactoring beyond the stated objectives.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into dev
Description check ✅ Passed PR描述清晰地说明了问题、解决方案和具体变更内容,与包含的代码变更高度相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/fake-200-error-logs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the system's ability to identify and report errors that are masked by a successful HTTP 200 status code from upstream providers. It introduces sophisticated content-based detection for various 'fake 200' scenarios, allowing the system to infer and utilize more accurate error status codes for internal logic like retries and circuit breaking. The user interface now provides clearer explanations for these errors, and a new configuration option enables more verbose error details in API responses for debugging.

Highlights

  • Enhanced Fake 200 Error Detection: Introduced more granular detection for 'fake 200' errors (where an upstream API returns a 200 status code but the response body indicates an error), including empty bodies, HTML error pages, and JSON bodies with error fields or keywords.
  • Inferred Status Codes: Implemented logic to infer a more semantically accurate HTTP status code (e.g., 401, 429, 404) from the content of fake 200 error bodies. This inferred status is used internally for circuit breaking and retry decisions.
  • Improved UI for Error Details: Updated the dashboard and provider chain popover to display the specific reasons for fake 200 errors and to clearly indicate when a status code has been inferred from the response body.
  • Resource Not Found (404) Handling: Added a new resource_not_found reason for 404 errors, which is explicitly excluded from circuit breaker failure counts to prevent misinterpreting resource absence as a provider failure.
  • Verbose Error Reporting: Introduced a system setting (verboseProviderError) that, when enabled, allows the API to return more detailed information about upstream errors, including sanitized raw body snippets, to aid in debugging.
  • Internationalization Updates: Added new translation strings across English, Japanese, Russian, Simplified Chinese, and Traditional Chinese to support the new error messages and inferred status code indicators.
Changelog
  • messages/en/dashboard.json
    • Added new keys for fake 200 detected reasons, inferred status code badge, tooltip, and suffix.
  • messages/en/provider-chain.json
    • Added new keys for 'Resource Not Found (404)' and related messages for timeline and notes.
  • messages/ja/dashboard.json
    • Added Japanese translations for fake 200 detected reasons, inferred status code badge, tooltip, and suffix.
  • messages/ja/provider-chain.json
    • Added Japanese translations for 'Resource Not Found (404)' and related messages.
  • messages/ru/dashboard.json
    • Added Russian translations for fake 200 detected reasons, inferred status code badge, tooltip, and suffix.
  • messages/ru/provider-chain.json
    • Added Russian translations for 'Resource Not Found (404)' and related messages.
  • messages/zh-CN/dashboard.json
    • Added Simplified Chinese translations for fake 200 detected reasons, inferred status code badge, tooltip, and suffix.
  • messages/zh-CN/provider-chain.json
    • Added Simplified Chinese translations for 'Resource Not Found (404)' and related messages.
  • messages/zh-TW/dashboard.json
    • Added Traditional Chinese translations for fake 200 detected reasons, inferred status code badge, tooltip, and suffix.
  • messages/zh-TW/provider-chain.json
    • Added Traditional Chinese translations for 'Resource Not Found (404)' and related messages.
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog.test.tsx
    • Updated test messages to include new fake 200 reasons.
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/LogicTraceTab.tsx
    • Updated getRequestStatus to include resource_not_found and endpoint_pool_exhausted as failure reasons.
    • Modified subtitle logic to display inferred status code suffix.
  • src/app/[locale]/dashboard/logs/_components/error-details-dialog/components/SummaryTab.tsx
    • Added getFake200ReasonKey to map internal fake 200 codes to display reasons.
    • Updated SummaryTab to display detected fake 200 reasons.
  • src/app/[locale]/dashboard/logs/_components/provider-chain-popover.test.tsx
    • Added new test cases for rendering inferred status code badge.
    • Updated test messages to include new fake 200 reasons and inferred status code details.
  • src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx
    • Updated isActualRequest and getItemStatus to include resource_not_found as a failure reason.
    • Added getFake200ReasonKey for displaying fake 200 reasons.
    • Implemented UI to show inferred status code badges and detailed fake 200 reasons in the popover.
  • src/app/v1/_lib/proxy/error-handler.ts
    • Imported getCachedSystemSettings and sanitizeErrorTextForDetail.
    • Modified handle method to conditionally attach verbose error details (including sanitized raw body) to the API response for fake 200 and empty response errors, based on verboseProviderError system setting.
  • src/app/v1/_lib/proxy/errors.ts
    • Extended ProxyError to include rawBody, rawBodyTruncated, statusCodeInferred, and statusCodeInferenceMatcherId in upstreamError details.
    • Modified getClientSafeMessage to provide more descriptive and sanitized messages for FAKE_200_ errors, including inferred status codes and redacted upstream details.
  • src/app/v1/_lib/proxy/forwarder.ts
    • Imported detectUpstreamErrorFromSseOrJsonText and inferUpstreamErrorStatusCodeFromText.
    • Added readResponseTextUpTo utility for inspecting non-streaming response bodies.
    • Enhanced non-streaming response handling to detect fake 200 errors (HTML body, JSON error fields) and infer status codes.
    • Updated ProxyError creation to include statusCodeInferred and statusCodeInferenceMatcherId.
    • Ensured EmptyResponseError is not swallowed by JSON parsing errors.
    • Added attemptNumber to endpoint_pool_exhausted chain items.
  • src/app/v1/_lib/proxy/response-handler.ts
    • Updated comments to reflect inferred status codes for fake 200 errors.
    • Modified finalizeDeferredStreamingFinalizationIfNeeded to infer status codes for fake 200 errors from stream content.
    • Modified finalizeDeferredStreamingFinalizationIfNeeded to use resource_not_found as a chain reason for inferred 404s, excluding them from circuit breaker failures.
  • src/app/v1/_lib/proxy/session.ts
    • Added statusCodeInferred field to ProviderChainItem metadata.
  • src/lib/utils/provider-chain-formatter.test.ts
    • Added new test suite for resource_not_found reason, verifying its rendering in summary, description, and timeline, including inferred status codes.
  • src/lib/utils/provider-chain-formatter.ts
    • Updated getProviderStatus and isActualRequest to include resource_not_found.
    • Added formatTimelineStatusCode to display inferred status codes.
    • Modified formatProviderDescription and formatProviderTimeline to handle and display resource_not_found details and inferred status codes.
  • src/lib/utils/upstream-error-detection.test.ts
    • Added new test cases for detectUpstreamErrorFromSseOrJsonText to cover HTML body detection and BOM handling.
    • Added a new test suite for inferUpstreamErrorStatusCodeFromText to verify status code inference from various error texts.
  • src/lib/utils/upstream-error-detection.ts
    • Added UpstreamErrorStatusInferenceResult type.
    • Introduced inferUpstreamErrorStatusCodeFromText function to infer HTTP status codes (e.g., 401, 429, 404) from error text based on keywords, primarily for fake 200 scenarios.
    • Added isLikelyHtmlDocument function to detect HTML content.
    • Modified detectUpstreamErrorFromSseOrJsonText to detect HTML bodies and handle UTF-8 BOM.
    • Exported sanitizeErrorTextForDetail.
  • src/types/message.ts
    • Added statusCodeInferred field to ProviderChainItem interface.
  • tests/unit/proxy/error-handler-verbose-provider-error-details.test.ts
    • Added new test file to verify verbose error details in ProxyErrorHandler, including raw body sanitization and interaction with error overrides.
  • tests/unit/proxy/proxy-forwarder-endpoint-audit.test.ts
    • Added a test case to ensure endpoint_pool_exhausted is not deduped when initial_selection is already recorded.
  • tests/unit/proxy/proxy-forwarder-fake-200-html.test.ts
    • Added new test file to cover fake 200 HTML body detection, JSON error detection with HTML content type, JSON error detection with Content-Length, inferred status codes for fake 200 errors, and handling of invalid Content-Length.
  • tests/unit/proxy/response-handler-endpoint-circuit-isolation.test.ts
    • Updated createSession to allow metadata in addProviderToChain.
    • Modified createFake200StreamResponse to accept an error message.
    • Added test cases for fake 200 inferred 401 and 404, verifying circuit breaker and chain updates.
Activity
  • The pull request introduces new features and fixes related to error handling and logging.
  • It includes updates to internationalization files across multiple languages.
  • New unit tests have been added to cover the enhanced error detection and reporting logic.
  • Existing components like LogicTraceTab, SummaryTab, and ProviderChainPopover have been updated to display the new error details.
  • Core proxy logic in error-handler.ts, errors.ts, forwarder.ts, and response-handler.ts has been modified to implement the fake 200 detection, status code inference, and verbose error reporting.
  • The ProviderChainItem type has been extended to support inferred status codes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added size/XL Extra Large PR (> 1000 lines) bug Something isn't working area:UI labels Feb 15, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly improves the detection and logging of "fake 200" errors, enhancing observability and troubleshooting with well-structured changes for error detection, handling, and presentation, including status code inference and extensive test coverage. However, a critical security issue was identified regarding the incomplete redaction of sensitive information in error messages returned to clients, which could lead to the exposure of PII or credentials. Additionally, there is a suggestion to improve code consistency and maintainability regarding data sanitization.

Comment on lines 522 to 525
const safe = clipped
.replace(/Bearer\s+[A-Za-z0-9._-]+/gi, "Bearer [REDACTED]")
.replace(/\b(?:sk|rk|pk)-[A-Za-z0-9_-]{16,}\b/giu, "[REDACTED_KEY]")
.replace(/\bAIza[0-9A-Za-z_-]{16,}\b/g, "[REDACTED_KEY]");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The getClientSafeMessage function uses an incomplete redaction logic for sensitive information, missing patterns like JWTs, emails, and generic secrets. This could lead to the exposure of PII or credentials to the client. For consistent and more secure sanitization, it is recommended to use the comprehensive sanitizeErrorTextForDetail function from @/lib/utils/upstream-error-detection instead of custom regex replacements. This will ensure all sensitive patterns are properly redacted.

        const safe = sanitizeErrorTextForDetail(clipped);

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@src/app/`[locale]/dashboard/logs/_components/error-details-dialog.test.tsx:
- Around line 256-264: The test is missing an assertion for the "Detected
reason" line; in the existing fake-200 test inside error-details-dialog.test.tsx
(the test that already asserts the forwarded notice), add an assertion that the
rendered output contains the fake200DetectedReason string with the specific
fake200Reasons[...] value used in that test (use the same reason key the test
supplies), e.g., assert that fake200DetectedReason.replace("{reason}",
fake200Reasons.<usedKey>) appears in the DOM alongside the forwarded notice;
update the test for the fake-200 case to include this extra expectation so the
new display logic is covered.

In
`@src/app/`[locale]/dashboard/logs/_components/error-details-dialog/components/LogicTraceTab.tsx:
- Around line 466-474: The subtitle currently hardcodes the "HTTP …" prefix;
update LogicTraceTab.tsx to use i18n by creating a translation key (e.g.
logicTrace.httpStatus) and call t(...) to format the status string instead of
literal `HTTP ${item.statusCode}`; ensure you still conditionally append the
inferred suffix via t("statusCodeInferredSuffix") when item.statusCodeInferred
is true and preserve the existing branches that fall back to item.name or
tChain(`reasons.${item.reason}`) so only the HTTP prefix is replaced with
t("logicTrace.httpStatus", { code: item.statusCode }) (or equivalent usage of t
for interpolation).

In `@src/app/`[locale]/dashboard/logs/_components/provider-chain-popover.test.tsx:
- Around line 89-100: Replace the single-quoted string for
jsonMessageKeywordMatch inside the fake200Reasons object with a double-quoted
string and escape the inner double quotes (e.g., change 'JSON message contains
"error"' to "JSON message contains \"error\""), then run Biome formatting so the
file (including fake200DetectedReason, statusCodeInferredBadge,
statusCodeInferredTooltip, statusCodeInferredSuffix and fake200Reasons) conforms
to the project's double-quote, trailing-commas, 2-space indent, and 100-char
width rules.

In `@src/app/v1/_lib/proxy/error-handler.ts`:
- Around line 252-286: The verbose-details block guarded by
shouldAttachVerboseDetails can throw if getCachedSystemSettings() or subsequent
property access fails; wrap the entire block (the await
getCachedSystemSettings() call and the branching that sets details for
ProxyError and isEmptyResponseError) in a try-catch so failures silently degrade
(leave details as undefined) and do not throw; keep the existing branches for
ProxyError and isEmptyResponseError (references: shouldAttachVerboseDetails,
getCachedSystemSettings, ProxyError, isEmptyResponseError,
sanitizeErrorTextForDetail, details, upstreamRequestId) and optionally log the
caught error as a non-fatal debug message rather than letting it propagate.

In `@src/app/v1/_lib/proxy/response-handler.ts`:
- Around line 133-140: The call to inferUpstreamErrorStatusCodeFromText on line
133 is failing because that function isn’t imported; add an import for
inferUpstreamErrorStatusCodeFromText to the top import block (alongside the
other imports) using the exact exported name so the file compiles and the
effectiveStatusCode/statusCodeInferenceMatcherId logic can use the function.
🧹 Nitpick comments (4)
tests/unit/proxy/error-handler-verbose-provider-error-details.test.ts (1)

36-46: createSession 使用 any 类型构造最小化 session 对象。

考虑到这是测试辅助函数,使用 any 简化了构造过程且不影响生产代码,可以接受。但与 proxy-forwarder-fake-200-html.test.ts 中使用 ProxySession.prototype 的方式相比,这里的方式更脆弱——如果 ProxyErrorHandler.handle 将来访问更多 session 属性,这里不会有编译时提示。

src/lib/utils/provider-chain-formatter.ts (1)

810-848: vendor_type_all_timeout 分支未使用 formatTimelineStatusCode,与其他分支不一致。

第 816 行和第 837 行直接调用 t("timeline.statusCode", ...) 而非 formatTimelineStatusCode(item, ...)。虽然当前 vendor_type_all_timeoutstatusCodeInferred 预期始终为 false,但为保持一致性和未来扩展性,建议统一使用新的辅助函数。

建议的修改
       if (item.errorDetails?.provider) {
         const p = item.errorDetails.provider;
         timeline += `${t("timeline.provider", { provider: p.name })}\n`;
-        timeline += `${t("timeline.statusCode", { code: p.statusCode })}\n`;
+        timeline += `${formatTimelineStatusCode(item, p.statusCode, t)}\n`;
         timeline += `${t("timeline.error", { error: p.statusText })}\n`;
       } else {
         timeline += `${t("timeline.provider", { provider: item.name })}\n`;
         if (item.statusCode) {
-          timeline += `${t("timeline.statusCode", { code: item.statusCode })}\n`;
+          timeline += `${formatTimelineStatusCode(item, item.statusCode, t)}\n`;
         }
src/app/[locale]/dashboard/logs/_components/provider-chain-popover.tsx (1)

63-78: getFake200ReasonKey 在多个组件中重复定义,建议提取为共享工具函数。

provider-chain-popover.tsxerror-details-dialog/components/SummaryTab.tsx 中存在相同实现的 getFake200ReasonKey 函数。虽然返回的 i18n 键前缀略有不同(logs.details.fake200Reasons.fake200Reasons.),但核心逻辑完全相同。将其提取到共享模块(如 @/lib/utils/fake-200-helpers.ts)中,可避免维护重复代码。后续新增 FAKE_200 类型时只需修改一处映射。

src/app/v1/_lib/proxy/forwarder.ts (1)

780-795: shouldInspectBodyisJson && !hasValidContentLength 时的覆盖存在隐含依赖

isJson=truehasValidContentLength=false 时:

  • shouldInspectJson = false(因为 hasValidContentLength 为 false)
  • shouldInspectBody = true(因为 !hasValidContentLength

此时仍会读取 body 并在后续 lines 797-830 对 JSON 内容做 detectUpstreamErrorFromSseOrJsonText 检测——这是正确的行为,但逻辑上 shouldInspectJson 的命名会让人误以为"JSON 不会被检查"。当前命名 shouldInspectJson 实际含义更接近"是否因 JSON 类型 主动 触发检查",而 !hasValidContentLength 是兜底路径。建议在注释中补充说明,或将变量名改为更明确的表述,以减少维护时的误读。

Comment on lines +89 to +100
fake200DetectedReason: "Detected reason: {reason}",
statusCodeInferredBadge: "Inferred",
statusCodeInferredTooltip: "This status code is inferred from response body content.",
statusCodeInferredSuffix: "(inferred)",
fake200Reasons: {
emptyBody: "Empty response body",
htmlBody: "HTML document returned",
jsonErrorNonEmpty: "JSON has non-empty error field",
jsonErrorMessageNonEmpty: "JSON has non-empty error.message",
jsonMessageKeywordMatch: 'JSON message contains "error"',
unknown: "Response body indicates an error",
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

请改为双引号以符合项目格式规范。

jsonMessageKeywordMatch 使用单引号会违背 Biome 的双引号规则,建议改为双引号并转义内部引号。

Proposed fix
-          jsonMessageKeywordMatch: 'JSON message contains "error"',
+          jsonMessageKeywordMatch: "JSON message contains \"error\"",

As per coding guidelines: Format code with Biome using: double quotes, trailing commas, 2-space indent, 100 character line width

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fake200DetectedReason: "Detected reason: {reason}",
statusCodeInferredBadge: "Inferred",
statusCodeInferredTooltip: "This status code is inferred from response body content.",
statusCodeInferredSuffix: "(inferred)",
fake200Reasons: {
emptyBody: "Empty response body",
htmlBody: "HTML document returned",
jsonErrorNonEmpty: "JSON has non-empty error field",
jsonErrorMessageNonEmpty: "JSON has non-empty error.message",
jsonMessageKeywordMatch: 'JSON message contains "error"',
unknown: "Response body indicates an error",
},
fake200DetectedReason: "Detected reason: {reason}",
statusCodeInferredBadge: "Inferred",
statusCodeInferredTooltip: "This status code is inferred from response body content.",
statusCodeInferredSuffix: "(inferred)",
fake200Reasons: {
emptyBody: "Empty response body",
htmlBody: "HTML document returned",
jsonErrorNonEmpty: "JSON has non-empty error field",
jsonErrorMessageNonEmpty: "JSON has non-empty error.message",
jsonMessageKeywordMatch: "JSON message contains \"error\"",
unknown: "Response body indicates an error",
},
🤖 Prompt for AI Agents
In `@src/app/`[locale]/dashboard/logs/_components/provider-chain-popover.test.tsx
around lines 89 - 100, Replace the single-quoted string for
jsonMessageKeywordMatch inside the fake200Reasons object with a double-quoted
string and escape the inner double quotes (e.g., change 'JSON message contains
"error"' to "JSON message contains \"error\""), then run Biome formatting so the
file (including fake200DetectedReason, statusCodeInferredBadge,
statusCodeInferredTooltip, statusCodeInferredSuffix and fake200Reasons) conforms
to the project's double-quote, trailing-commas, 2-space indent, and 100-char
width rules.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/app/v1/_lib/proxy/response-handler.ts`:
- Around line 255-272: The 404 exemption is inconsistent: update the Gemini
passthrough and non-stream failure branches (the places that currently check if
(statusCode >= 400)) to match the Deferred path's logic by excluding 404 from
circuit-breaker recording; specifically, change those conditions to skip calling
recordFailure (or invoking the circuit-breaker import) when statusCode === 404
(or use the same effectiveStatusCode/chainReason logic), and add/update a
comment mirroring the Deferred path note that 404 denotes RESOURCE_NOT_FOUND and
should not be counted as provider failure; ensure the same recordFailure usage
(imported "@/lib/circuit-breaker" and recordFailure(meta.providerId, new
Error(detected.code))) is applied after the condition change.
🧹 Nitpick comments (1)
src/app/v1/_lib/proxy/response-handler.ts (1)

305-334: 非 200 流式路径的 404 处理逻辑一致,LGTM。

正确复用了与假 200 路径相同的 404 豁免策略,且未传递 statusCodeInferred(因为此路径中状态码是实际 HTTP 状态,非推断值)。

小建议:第 255 行和第 305 行的 chainReason 推导逻辑完全相同,可考虑提取为辅助函数以减少重复。

♻️ 可选重构:提取 chainReason 推导
+function deriveChainReason(effectiveStatusCode: number): "resource_not_found" | "retry_failed" {
+  return effectiveStatusCode === 404 ? "resource_not_found" : "retry_failed";
+}
+
 // Line 255:
-const chainReason = effectiveStatusCode === 404 ? "resource_not_found" : "retry_failed";
+const chainReason = deriveChainReason(effectiveStatusCode);

 // Line 305:
-const chainReason = effectiveStatusCode === 404 ? "resource_not_found" : "retry_failed";
+const chainReason = deriveChainReason(effectiveStatusCode);

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

29 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 115 to 118
{
statusCode: 404,
matcherId: "not_found",
re: /(?:\bHTTP\/\d(?:\.\d)?\s+404\b|\bnot\s+found\b|\b(?:model|deployment|endpoint|resource)\s+not\s+found\b|\bunknown\s+model\b|\bdoes\s+not\s+exist\b|\bNOT_FOUND\b|\bResourceNotFoundException\b|未找到|不存在|模型不存在)/iu,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broad not_found regex may over-match

The bare \bnot\s+found\b alternative in this regex is quite broad. Since this matcher runs on the full response body text (up to 64 KiB), it could match on error messages like "Solution not found" or "Key not found in cache" that are not HTTP 404 semantics, causing a fake-200 to be incorrectly inferred as 404 instead of the default 502.

While this only runs after detectUpstreamErrorFromSseOrJsonText has confirmed isError=true, a mis-inferred 404 has meaningful consequences: it changes the chain reason to resource_not_found and skips circuit breaker recording (since 404 is treated as "resource doesn't exist, not a provider fault").

Consider tightening the bare match to require more context, e.g. requiring it to appear near an HTTP status reference, or removing the bare \bnot\s+found\b and relying on the more specific alternatives (model not found, endpoint not found, NOT_FOUND, ResourceNotFoundException, etc.).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/utils/upstream-error-detection.ts
Line: 115:118

Comment:
**Broad `not_found` regex may over-match**

The bare `\bnot\s+found\b` alternative in this regex is quite broad. Since this matcher runs on the full response body text (up to 64 KiB), it could match on error messages like `"Solution not found"` or `"Key not found in cache"` that are not HTTP 404 semantics, causing a fake-200 to be incorrectly inferred as 404 instead of the default 502.

While this only runs after `detectUpstreamErrorFromSseOrJsonText` has confirmed `isError=true`, a mis-inferred 404 has meaningful consequences: it changes the chain reason to `resource_not_found` and **skips** circuit breaker recording (since 404 is treated as "resource doesn't exist, not a provider fault").

Consider tightening the bare match to require more context, e.g. requiring it to appear near an HTTP status reference, or removing the bare `\bnot\s+found\b` and relying on the more specific alternatives (`model not found`, `endpoint not found`, `NOT_FOUND`, `ResourceNotFoundException`, etc.).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +131 to +133
statusCode: 409,
matcherId: "conflict",
re: /(?:\bHTTP\/\d(?:\.\d)?\s+409\b|\bconflict\b|\bidempotency(?:-key)?\b|\bABORTED\b|冲突|幂等)/iu,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broad conflict regex may over-match

The \bconflict\b alternative is very broad and could match on error messages that happen to contain the word "conflict" in a non-409 context (e.g., "merge conflict detected" in a CI/CD error page, or "version conflict" in a cache layer error). Since 409 Conflict is rarely returned by LLM API providers, the risk is low, but worth noting for future maintenance.

Consider requiring more specific context, e.g. \bHTTP\/\d(?:\.\d)?\s+409\b|\bidempotency(?:-key)?\b|\bABORTED\b without the bare \bconflict\b.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/utils/upstream-error-detection.ts
Line: 131:133

Comment:
**Broad `conflict` regex may over-match**

The `\bconflict\b` alternative is very broad and could match on error messages that happen to contain the word "conflict" in a non-409 context (e.g., `"merge conflict detected"` in a CI/CD error page, or `"version conflict"` in a cache layer error). Since 409 Conflict is rarely returned by LLM API providers, the risk is low, but worth noting for future maintenance.

Consider requiring more specific context, e.g. `\bHTTP\/\d(?:\.\d)?\s+409\b|\bidempotency(?:-key)?\b|\bABORTED\b` without the bare `\bconflict\b`.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

let statusCodeInferenceMatcherId: string | undefined;
if (detected.isError) {
effectiveStatusCode = 502;
const inferred = inferUpstreamErrorStatusCodeFromText(allContent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] [LOGIC-BUG] Missing import for inferUpstreamErrorStatusCodeFromText

src/app/v1/_lib/proxy/response-handler.ts:133

Problematic code:

const inferred = inferUpstreamErrorStatusCodeFromText(allContent);

Why this is a problem: inferUpstreamErrorStatusCodeFromText is not imported in this module, so this will fail compilation and block the build.

Suggested fix:

import {
  detectUpstreamErrorFromSseOrJsonText,
  inferUpstreamErrorStatusCodeFromText,
} from "@/lib/utils/upstream-error-detection";

Confidence: 95/100

if (truncated) {
try {
await reader.cancel();
} catch {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[High] [ERROR-SILENT] readResponseTextUpTo swallows cleanup errors

src/app/v1/_lib/proxy/forwarder.ts:151 and src/app/v1/_lib/proxy/forwarder.ts:158

Problematic code:

} catch {
  // ignore
}

Why this is a problem: If reader.cancel() / reader.releaseLock() fails, the error is silently swallowed, which hides stream/reader issues and makes production failures harder to diagnose.

Suggested fix (log at debug-level and keep behavior the same):

} catch (cancelError) {
  logger.debug("ProxyForwarder: Failed to cancel non-stream inspection reader", {
    cancelError,
  });
}

// ...

} catch (releaseError) {
  logger.debug("ProxyForwarder: Failed to release non-stream inspection reader lock", {
    releaseError,
  });
}

Confidence: 85/100

@github-actions
Copy link
Contributor

Code Review Summary

This PR implements comprehensive "fake 200" error detection and logging improvements for the proxy system. The changes significantly enhance the system's ability to detect when upstream providers return HTTP 200 with error content (HTML error pages, JSON error objects, or empty bodies).

PR Size: XL

  • Lines changed: ~2,000 (1,923 additions, 78 deletions)
  • Files changed: 29
  • Recommendation: Given the complexity and cross-cutting nature of these changes, the PR is appropriately sized for the feature being delivered. Consider breaking future similar work into: (1) core detection logic, (2) proxy integration, (3) UI components.

Critical Issues Found

Severity Category Description
Critical Missing Import getCachedSystemSettings and sanitizeErrorTextForDetail are used in error-handler.ts but not imported

Fix required in src/app/v1/_lib/proxy/error-handler.ts:

// Add these imports at the top of the file
import { getCachedSystemSettings } from "@/lib/config/system-settings-cache";
import { sanitizeErrorTextForDetail } from "@/lib/utils/upstream-error-detection";

Impact: Without these imports, the error handler will throw runtime errors when processing FAKE_200 errors with verboseProviderError enabled.

Review Coverage

  • Logic and correctness - Good overall, one critical issue identified
  • Security (OWASP Top 10) - Raw body sanitization properly implemented
  • Error handling - Comprehensive fake-200 detection added
  • Type safety - TypeScript types properly extended
  • Documentation accuracy - Comments and i18n complete
  • Test coverage - Unit tests added for new functionality
  • Code clarity - Well-structured with clear separation of concerns

Notable Positive Aspects

  1. Security-conscious design: Raw body content is properly sanitized before being included in error responses
  2. Comprehensive i18n: All user-facing strings properly internationalized for 5 languages
  3. Test coverage: Good unit test coverage for the new fake-200 detection scenarios
  4. Documentation: Clear comments explaining the detection logic and design decisions

Recommendation

Request changes - Please add the missing imports in error-handler.ts before merging.


Automated review by Claude AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This PR makes broad changes across core proxy error classification (fake-200 detection + inferred status codes), dashboard rendering/i18n strings, and adds a large amount of new tests. Two high-confidence issues were found: a missing import that will block compilation, and new silent catch blocks that swallow cleanup errors.

PR Size: XL

  • Lines changed: 1996
  • Files changed: 29
  • Split suggestion: Separate into (1) proxy fake-200 detection/inference changes (+ unit tests), (2) dashboard UI + i18n message updates, (3) provider-chain formatter changes (+ formatter tests).

Issues Found

Category Critical High Medium Low
Logic/Bugs 1 0 0 0
Security 0 0 0 0
Error Handling 0 1 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Critical Issues (Must Fix)

  • src/app/v1/_lib/proxy/response-handler.ts:133 — Missing import for inferUpstreamErrorStatusCodeFromText (Confidence: 95/100)

High Priority Issues (Should Fix)

  • src/app/v1/_lib/proxy/forwarder.ts:151readResponseTextUpTo swallows cleanup errors (reader.cancel() / reader.releaseLock()) (Confidence: 85/100)

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10)
  • Error handling
  • Type safety
  • Documentation accuracy
  • Test coverage
  • Code clarity

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Identified open PR #790 and applied the label size/XL (1996 lines changed across 29 files).
  • Posted 2 inline review comments on diff lines:
    • src/app/v1/_lib/proxy/response-handler.ts:133Critical missing import for inferUpstreamErrorStatusCodeFromText (build-blocking).
    • src/app/v1/_lib/proxy/forwarder.ts:151High silent catch { /* ignore */ } swallowing reader.cancel() / reader.releaseLock() cleanup errors.
  • Submitted the required “Code Review Summary” as a PR review comment via gh pr review.

tesgth032 and others added 3 commits February 15, 2026 23:30
* fix(proxy): 识别 200+HTML 假200并触发故障转移

* fix(utils): 收紧 HTML 文档识别避免误判

* fix(proxy): 非流式假200补齐强信号 JSON error 检测

* fix(utils): 假200检测兼容 BOM

* perf(proxy): 降低非流式嗅探读取上限

* fix(proxy): 客户端隐藏 FAKE_200_* 内部码

* fix(logs): 补齐 endpoint_pool_exhausted/404 错因展示

- endpoint_pool_exhausted 写入 attemptNumber,避免被 initial_selection/session_reuse 去重吞掉\n- 决策链/技术时间线补齐 resource_not_found 的失败态与说明\n- 更新 provider-chain i18n 文案并新增单测覆盖

* fix(proxy): 非流式 JSON 假200检测覆盖 Content-Length

- 对 application/json 且 Content-Length<=32KiB 的 2xx 响应也做强信号嗅探\n- 补齐 200+JSON error(带 Content-Length)触发故障转移的回归测试

* chore: format code (fix-issue-749-fake-200-html-detection-005fad3)

* fix(i18n): 修正 ru 端点池耗尽文案

- 修正俄语中 endpoint 的复数属格拼写(конечных точек)\n- 不影响 key,仅更新展示文案

* test(formatter): 补齐 resource_not_found 组合场景覆盖

- 覆盖 resource_not_found + retry_success 多供应商链路\n- 覆盖缺少 errorDetails.provider 的降级渲染路径

* fix(proxy): FAKE_200 客户端提示附带脱敏片段

* fix: 改进 FAKE_200 错误原因提示

* fix(proxy): verboseProviderError 回传假200原文

- fake-200/空响应:verboseProviderError 开启时在 error.details 返回详细报告与上游原文(不落库)\n- forwarder: 将检测到的原文片段挂到 ProxyError.upstreamError.rawBody\n- tests: 覆盖 verbose details 与 rawBody 透传

* fix(proxy): 强化 Content-Length 校验与假200片段防泄露

- forwarder: 将非法 Content-Length 视为无效,避免漏检 HTML/空响应\n- errors: FAKE_200 客户端 detail 二次截断 + 轻量脱敏(防御性)\n- tests: 覆盖非法 Content-Length 漏检回归

* docs(proxy): 说明非流式假200检测上限

* docs(settings): 补充 verboseProviderError 安全提示

* fix(proxy): verboseProviderError rawBody 基础脱敏

* chore: format code (fix-issue-749-fake-200-html-detection-b56b790)

* docs(settings): 说明 verboseProviderError 基础脱敏

* fix(proxy/logs): 假200 推断状态码并显著标记

* fix(i18n): 回退 verboseProviderErrorDesc 原始文案

* fix(stream): 404 资源不存在不计入熔断

---------

Co-authored-by: tesgth032 <tesgth032@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ding <44717411+ding113@users.noreply.github.com>
The function inferUpstreamErrorStatusCodeFromText was used in
response-handler.ts but was not imported, causing a TypeScript
compilation error during build.

Fixed:
- Added inferUpstreamErrorStatusCodeFromText to imports from @/lib/utils/upstream-error-detection

CI Run: https://github.com/ding113/claude-code-hub/actions/runs/22033028838
…ing sanitization

Extract duplicated getFake200ReasonKey() from SummaryTab and
ProviderChainPopover into a shared fake200-reason.ts utility,
eliminating the risk of silent drift when new FAKE_200_* codes are added.

Replace the 3-pattern manual sanitization in getClientSafeMessage()
with the existing sanitizeErrorTextForDetail() (6 patterns), closing
a gap where JWT tokens, emails, and password/config paths could leak
to clients via the FAKE_200 error detail path.

Add unit tests verifying JWT, email, and password sanitization.
@ding113 ding113 force-pushed the refactor/fake-200-error-logs branch from 83bf797 to 3db096d Compare February 15, 2026 15:49
@github-actions
Copy link
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +160 to +164
{
statusCode: 500,
matcherId: "internal_server_error",
re: /(?:\bHTTP\/\d(?:\.\d)?\s+500\b|\binternal\s+server\s+error\b|\bInternalServerException\b|\bINTERNAL\b|内部错误|服务器错误)/iu,
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broad \bINTERNAL\b with case-insensitive flag

With the /iu flags, \bINTERNAL\b matches the word "internal" in any case (e.g., "internal configuration error", "internal model inference failed"). While this matcher is near the bottom of the priority list (position 14 of 15) and only runs on already-confirmed errors, a match could cause a mis-categorization as 500 when the actual error might be better mapped to a different status code (e.g., a 400-level error that happens to contain the word "internal").

Consider making this case-sensitive (matching only gRPC-style INTERNAL) or requiring more context, similar to how other matchers require surrounding keywords:

Suggested change
{
statusCode: 500,
matcherId: "internal_server_error",
re: /(?:\bHTTP\/\d(?:\.\d)?\s+500\b|\binternal\s+server\s+error\b|\bInternalServerException\b|\bINTERNAL\b||)/iu,
},
{
statusCode: 500,
matcherId: "internal_server_error",
re: /(?:\bHTTP\/\d(?:\.\d)?\s+500\b|\binternal\s+server\s+error\b|\bInternalServerException\b|\bINTERNAL\b(?<![a-z])||)/iu,
},

Note: the suggestion above uses a negative lookbehind (?<![a-z]) to match INTERNAL only when not preceded by a lowercase letter, which would require the all-caps form. Alternatively, you could replace \bINTERNAL\b with a standalone case-sensitive check or require it to appear as a standalone gRPC status (e.g., "status":\s*"INTERNAL"). This is low-priority given its position in the matcher list.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/utils/upstream-error-detection.ts
Line: 160:164

Comment:
**Broad `\bINTERNAL\b` with case-insensitive flag**

With the `/iu` flags, `\bINTERNAL\b` matches the word "internal" in any case (e.g., `"internal configuration error"`, `"internal model inference failed"`). While this matcher is near the bottom of the priority list (position 14 of 15) and only runs on already-confirmed errors, a match could cause a mis-categorization as 500 when the actual error might be better mapped to a different status code (e.g., a 400-level error that happens to contain the word "internal").

Consider making this case-sensitive (matching only gRPC-style `INTERNAL`) or requiring more context, similar to how other matchers require surrounding keywords:

```suggestion
  {
    statusCode: 500,
    matcherId: "internal_server_error",
    re: /(?:\bHTTP\/\d(?:\.\d)?\s+500\b|\binternal\s+server\s+error\b|\bInternalServerException\b|\bINTERNAL\b(?<![a-z])|内部错误|服务器错误)/iu,
  },
```

Note: the suggestion above uses a negative lookbehind `(?<![a-z])` to match `INTERNAL` only when not preceded by a lowercase letter, which would require the all-caps form. Alternatively, you could replace `\bINTERNAL\b` with a standalone case-sensitive check or require it to appear as a standalone gRPC status (e.g., `"status":\s*"INTERNAL"`). This is low-priority given its position in the matcher list.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/app/v1/_lib/proxy/error-handler.ts`:
- Around line 257-260: 当前对 error.upstreamError.rawBody 的处理会对非空字符串先调用
sanitizeErrorTextForDetail 但不限制长度,导致超长内容(如大 HTML)可被放入返回的 details。修改 rawBody
的赋值流程:若为字符串,先调用 sanitizeErrorTextForDetail(rawBody) 再对结果执行截断(复用或与
getClientSafeMessage() 相同的最大长度常量/策略),将截断后的字符串赋回用于 details;保留对非字符串 rawBody
的原有行为。使用标识符:rawBody、sanitizeErrorTextForDetail、getClientSafeMessage、details。

In `@src/lib/utils/upstream-error-detection.ts`:
- Around line 94-170: The not_found matcher in ERROR_STATUS_MATCHERS is too
broad (it uses \bnot\s+found\b) and can false-positive generic messages like
"Configuration key not found"; update the entry with matcherId "not_found"
inside ERROR_STATUS_MATCHERS to remove the generic \bnot\s+found\b and tighten
the re to only match contextual patterns (e.g.,
\b(model|deployment|endpoint|resource|service|user)\s+not\s+found\b and explicit
HTTP\/\d+\s+404 patterns) so only real resource-missing responses trigger the
404 classification.
🧹 Nitpick comments (4)
src/lib/utils/provider-chain-formatter.ts (1)

813-837: vendor_type_all_timeout 分支未使用 formatTimelineStatusCode,与其他分支不一致。

retry_failedresource_not_foundclient_error_non_retryable 均已统一使用 formatTimelineStatusCode,但 vendor_type_all_timeout 的两处(第816行和第837行)仍直接调用 t("timeline.statusCode", ...)。虽然当前 vendor_type_all_timeout 不太可能出现 statusCodeInferred=true 的场景,但为了一致性和未来扩展性,建议统一使用 helper。

建议修改
       if (item.errorDetails?.provider) {
         const p = item.errorDetails.provider;
         timeline += `${t("timeline.provider", { provider: p.name })}\n`;
-        timeline += `${t("timeline.statusCode", { code: p.statusCode })}\n`;
+        timeline += `${formatTimelineStatusCode(item, p.statusCode, t)}\n`;
         timeline += `${t("timeline.error", { error: p.statusText })}\n`;
       } else {
         timeline += `${t("timeline.provider", { provider: item.name })}\n`;
         if (item.statusCode) {
-          timeline += `${t("timeline.statusCode", { code: item.statusCode })}\n`;
+          timeline += `${formatTimelineStatusCode(item, item.statusCode, t)}\n`;
         }
src/app/v1/_lib/proxy/errors.ts (1)

483-528: getClientSafeMessage() 中 FAKE_200 分支的防御性处理值得肯定,但有一个小建议。

整体逻辑合理:reason 映射 → inferred note → 防御性二次脱敏/截断。不过 maxChars = 200 是一个有业务含义的阈值,建议提取为模块级常量,方便后续统一调整。

建议提取常量
+/** getClientSafeMessage() 中"上游 detail 片段"的最大字符数 */
+const CLIENT_SAFE_DETAIL_MAX_CHARS = 200;
+
 // 在 getClientSafeMessage 内部
-        const maxChars = 200;
+        const maxChars = CLIENT_SAFE_DETAIL_MAX_CHARS;
src/lib/utils/upstream-error-detection.ts (1)

160-164: internal_server_error\bINTERNAL\b 匹配过宽。

\bINTERNAL\b 会匹配任何包含 "internal" 单词的文本(因为 /iu 标志),包括 "INTERNAL_ERROR" 以及 "An internal process has..." 等非 500 的上下文。由于该规则排在列表靠后位置(优先级低),实际误判概率不高,但仍建议收窄为更具上下文的模式,如 \binternal\s+(?:server\s+)?error\b\bINTERNAL_ERROR\b

src/app/v1/_lib/proxy/forwarder.ts (1)

832-839: 缺失 Content-Length 时,inspectedText 保证已被赋值,但依赖隐式推导——建议添加断言注释。

当进入 !contentLength || !hasValidContentLength 分支时,shouldInspectBody 一定为 true(因为 !hasValidContentLength 是其条件之一),所以 inspectedText 一定已被赋值。但这个推导链不够显式,后续维护者可能不清楚为何 inspectedText ?? "" 的 fallback 不会被触发。

建议添加注释
          if (!contentLength || !hasValidContentLength) {
-           const responseText = inspectedText ?? "";
+           // shouldInspectBody 包含 !hasValidContentLength 条件,所以此处 inspectedText 一定已被赋值。
+           // ?? "" 仅作防御性兜底。
+           const responseText = inspectedText ?? "";

- Add i18n for HTTP status prefix in LogicTraceTab (5 languages)
- Wrap verbose details gathering in try-catch to prevent cascading failures
- Truncate rawBody to 4096 chars before sanitization in error-handler
- Tighten not_found regex to require contextual prefixes, preventing false 404 inference
- Add debug logging to silent catch blocks in readResponseTextUpTo
- Add test assertion for fake200DetectedReason display
@github-actions
Copy link
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/app/`[locale]/dashboard/logs/_components/error-details-dialog.test.tsx:
- Around line 259-264: The string value for the jsonMessageKeywordMatch entry
uses single quotes and should use double quotes according to the project's Biome
formatting rules; update the value for jsonMessageKeywordMatch in the same
object (the mapping with keys emptyBody, htmlBody, jsonErrorNonEmpty,
jsonErrorMessageNonEmpty, jsonMessageKeywordMatch, unknown) to use double quotes
and escape the internal quotes (e.g., JSON message contains \"error\"),
preserving the trailing comma and existing indentation.
🧹 Nitpick comments (3)
src/lib/utils/upstream-error-detection.ts (1)

130-134: conflict 匹配器中 \bconflict\b 覆盖面偏宽

\bconflict\b 会匹配任何包含 "conflict" 的错误文本(如 "merge conflict in configuration""conflicting parameters"),可能导致非 409 场景被误推断为 409。虽然该函数仅在已判定 isError=true 后才调用,但误推断的状态码会影响熔断/故障转移决策。

建议收紧为带上下文的模式,例如 \b(?:version|resource|state|write)\s+conflict\b\bidempotency[-_ ]?conflict\b,与 not_found 的收紧策略保持一致。

src/app/v1/_lib/proxy/error-handler.ts (1)

248-250: startsWith("FAKE_200_")FAKE_200_CODES 常量存在隐式耦合

shouldAttachVerboseDetails 依赖 error.message.startsWith("FAKE_200_") 判断是否为假 200 错误。这与 upstream-error-detection.tsFAKE_200_CODES 的值格式存在隐式耦合——如果后续有人修改了 code 前缀,此处会静默失效。

建议从 upstream-error-detection.ts 导出一个辅助判断函数(如 isFake200Code(code: string): boolean)或导出 FAKE_200_CODES 常量,让两处逻辑通过同一来源保持一致。

建议方案

upstream-error-detection.ts 中新增:

export function isFake200Code(code: string): boolean {
  return Object.values(FAKE_200_CODES).includes(code as (typeof FAKE_200_CODES)[keyof typeof FAKE_200_CODES]);
}

然后在 error-handler.ts 中:

- const shouldAttachVerboseDetails =
-   (error instanceof ProxyError && error.message.startsWith("FAKE_200_")) ||
-   isEmptyResponseError(error);
+ const shouldAttachVerboseDetails =
+   (error instanceof ProxyError && isFake200Code(error.message)) ||
+   isEmptyResponseError(error);
src/app/v1/_lib/proxy/forwarder.ts (1)

780-784: shouldInspectBody 条件:未覆盖非标准 Content-Type 的假 200 场景

当 Content-Type 既非 HTML、也非 JSON,且有合法 Content-Length 时(例如 text/plaintext/xml),shouldInspectBodyfalse,不会进行假 200 检测。

根据注释,这是有意为之(避免对大体积非 JSON 响应做无谓检查)。但如果上游以 text/plain 返回 200 + 错误文本,此路径会漏检。建议在注释中补充说明这一设计取舍,便于后续维护者理解。

Comment on lines +259 to +264
emptyBody: "Empty response body",
htmlBody: "HTML document returned",
jsonErrorNonEmpty: "JSON has non-empty error field",
jsonErrorMessageNonEmpty: "JSON has non-empty error.message",
jsonMessageKeywordMatch: 'JSON message contains "error"',
unknown: "Response body indicates an error",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

字符串引号需统一为双引号

Line 263 使用单引号,违反当前代码格式规范。建议改为双引号并转义内部引号。

修改建议
-          jsonMessageKeywordMatch: 'JSON message contains "error"',
+          jsonMessageKeywordMatch: "JSON message contains \"error\"",

As per coding guidelines: Format code with Biome using: double quotes, trailing commas, 2-space indent, 100 character line width.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
emptyBody: "Empty response body",
htmlBody: "HTML document returned",
jsonErrorNonEmpty: "JSON has non-empty error field",
jsonErrorMessageNonEmpty: "JSON has non-empty error.message",
jsonMessageKeywordMatch: 'JSON message contains "error"',
unknown: "Response body indicates an error",
emptyBody: "Empty response body",
htmlBody: "HTML document returned",
jsonErrorNonEmpty: "JSON has non-empty error field",
jsonErrorMessageNonEmpty: "JSON has non-empty error.message",
jsonMessageKeywordMatch: "JSON message contains \"error\"",
unknown: "Response body indicates an error",
🤖 Prompt for AI Agents
In `@src/app/`[locale]/dashboard/logs/_components/error-details-dialog.test.tsx
around lines 259 - 264, The string value for the jsonMessageKeywordMatch entry
uses single quotes and should use double quotes according to the project's Biome
formatting rules; update the value for jsonMessageKeywordMatch in the same
object (the mapping with keys emptyBody, htmlBody, jsonErrorNonEmpty,
jsonErrorMessageNonEmpty, jsonMessageKeywordMatch, unknown) to use double quotes
and escape the internal quotes (e.g., JSON message contains \"error\"),
preserving the trailing comma and existing indentation.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +834 to +839
if (!contentLength || !hasValidContentLength) {
const responseText = inspectedText ?? "";

if (!responseText || responseText.trim() === "") {
throw new EmptyResponseError(currentProvider.id, currentProvider.name, "empty_body");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing body read when inspectedText is undefined

When shouldInspectBody is false (e.g., valid Content-Length + non-HTML/non-JSON content type), inspectedText remains undefined. This branch then checks !responseText where responseText = inspectedText ?? "", which evaluates to empty string, causing an EmptyResponseError to be thrown even when the response has a valid non-zero Content-Length.

Example: Content-Type: text/plain, Content-Length: 100 would skip body inspection but then throw empty response error.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/v1/_lib/proxy/forwarder.ts
Line: 834:839

Comment:
Missing body read when `inspectedText` is undefined

When `shouldInspectBody` is `false` (e.g., valid Content-Length + non-HTML/non-JSON content type), `inspectedText` remains `undefined`. This branch then checks `!responseText` where `responseText = inspectedText ?? ""`, which evaluates to empty string, causing an `EmptyResponseError` to be thrown even when the response has a valid non-zero Content-Length.

Example: `Content-Type: text/plain`, `Content-Length: 100` would skip body inspection but then throw empty response error.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +780 to +784
const shouldInspectJson =
isJson &&
hasValidContentLength &&
contentLengthBytes <= NON_STREAM_BODY_INSPECTION_MAX_BYTES;
const shouldInspectBody = isHtml || !hasValidContentLength || shouldInspectJson;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Body inspection logic may be redundant for valid Content-Length

When hasValidContentLength is true and contentLengthBytes > 0, the code still inspects the body if content type is HTML or if it's a small JSON. However, if a valid Content-Length header exists and is non-zero, the empty response check below (line 837) is already protected. Consider whether HTML/JSON inspection is needed when Content-Length is valid and non-zero, or clarify with comments.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/v1/_lib/proxy/forwarder.ts
Line: 780:784

Comment:
Body inspection logic may be redundant for valid Content-Length

When `hasValidContentLength` is true and `contentLengthBytes > 0`, the code still inspects the body if content type is HTML or if it's a small JSON. However, if a valid Content-Length header exists and is non-zero, the empty response check below (line 837) is already protected. Consider whether HTML/JSON inspection is needed when Content-Length is valid and non-zero, or clarify with comments.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 89 to +163
type CacheTtlOption = CacheTtlPreference | null | undefined;

// 非流式响应体检查的上限(字节):避免上游在 2xx 场景返回超大内容导致内存占用失控。
// 说明:
// - 该检查仅用于“空响应/假 200”启发式判定,不用于业务逻辑解析;
// - 超过上限时,仍认为“非空”,但会跳过 JSON 内容结构检查(避免截断导致误判)。
const NON_STREAM_BODY_INSPECTION_MAX_BYTES = 32 * 1024; // 32 KiB

/**
* 读取响应体文本,但最多读取 `maxBytes` 字节(用于非流式 2xx 的“空响应/假 200”嗅探)。
*
* 注意:
* - 该函数只用于启发式检测,不用于业务逻辑解析;
* - 超过上限时会 `cancel()` reader,避免继续占用资源;
* - 调用方应使用 `response.clone()`,避免消费掉原始响应体,影响后续透传/解析。
*/
async function readResponseTextUpTo(
response: Response,
maxBytes: number
): Promise<{ text: string; truncated: boolean }> {
const reader = response.body?.getReader();
if (!reader) {
return { text: "", truncated: false };
}

const decoder = new TextDecoder();
const chunks: string[] = [];
let bytesRead = 0;
let truncated = false;

try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (!value || value.byteLength === 0) continue;

const remaining = maxBytes - bytesRead;
// 注意:remaining<=0 发生在“已经读到下一块 chunk”之后。
// 对启发式嗅探而言,直接标记 truncated 并退出即可(等价于丢弃超出上限的后续字节),
// 避免对超出部分做无谓的解码开销。
if (remaining <= 0) {
truncated = true;
break;
}

if (value.byteLength > remaining) {
chunks.push(decoder.decode(value.subarray(0, remaining), { stream: true }));
bytesRead += remaining;
truncated = true;
break;
}

chunks.push(decoder.decode(value, { stream: true }));
bytesRead += value.byteLength;
}

const flushed = decoder.decode();
if (flushed) chunks.push(flushed);
} finally {
if (truncated) {
try {
await reader.cancel();
} catch (cancelErr) {
logger.debug("readResponseTextUpTo: failed to cancel reader", { error: cancelErr });
}
}

try {
reader.releaseLock();
} catch (releaseErr) {
logger.debug("readResponseTextUpTo: failed to release reader lock", { error: releaseErr });
}
}

return { text: chunks.join(""), truncated };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting readResponseTextUpTo to a shared utility

This 75-line function provides reusable stream reading functionality that could benefit other parts of the codebase. Consider moving it to a utility module (e.g., src/lib/utils/response-reader.ts) for better code organization and reusability.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/v1/_lib/proxy/forwarder.ts
Line: 89:163

Comment:
Consider extracting `readResponseTextUpTo` to a shared utility

This 75-line function provides reusable stream reading functionality that could benefit other parts of the codebase. Consider moving it to a utility module (e.g., `src/lib/utils/response-reader.ts`) for better code organization and reusability.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link

greptile-apps bot commented Feb 15, 2026

Additional Comments (2)

src/lib/utils/upstream-error-detection.ts
Bearer token regex may not cover all formats

/Bearer\s+[A-Za-z0-9._-]+/gi won't match tokens containing other valid base64url characters or longer character sets. Some providers use different token formats. Verify this covers your actual token formats, or document known limitations.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/utils/upstream-error-detection.ts
Line: 245:245

Comment:
Bearer token regex may not cover all formats

`/Bearer\s+[A-Za-z0-9._-]+/gi` won't match tokens containing other valid base64url characters or longer character sets. Some providers use different token formats. Verify this covers your actual token formats, or document known limitations.

How can I resolve this? If you propose a fix, please make it concise.

src/lib/utils/upstream-error-detection.ts
Sensitive key-value regex requires colon or equals

Pattern only matches key-value pairs with : or =. Won't sanitize JSON format like {"password":"value"} or certain query params. Consider handling quoted JSON values separately.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/utils/upstream-error-detection.ts
Line: 262:264

Comment:
Sensitive key-value regex requires colon or equals

Pattern only matches key-value pairs with `:` or `=`. Won't sanitize JSON format like `{"password":"value"}` or certain query params. Consider handling quoted JSON values separately.

How can I resolve this? If you propose a fix, please make it concise.

@ding113 ding113 merged commit aacaae4 into dev Feb 15, 2026
16 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Feb 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:UI bug Something isn't working size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants