feat(error-rules): add Gemini non-streaming error rule#547
feat(error-rules): add Gemini non-streaming error rule#547ding113 merged 2 commits intoding113:mainfrom
Conversation
部分服务商仅支持流式的 gemini 请求, 这种情况下需要返回 400 状态码, 以便 gemini 检测到 generateContent 不可用时, 自动切换到 streamGenerateContent Signed-off-by: Kevin Cui <bh@bugs.cc>
Summary of ChangesHello @BlackHole1, 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 introduces a new default error rule to enhance compatibility with Gemini service providers that exclusively support streaming requests. By detecting specific error messages related to unsupported non-streaming requests, the system now returns a 400 Bad Request status, which prompts Gemini to automatically utilize its streaming Highlights
Using Gemini Code AssistThe 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
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 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
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthrough在 Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro Cache: Disabled due to Reviews > Disable Cache setting 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Comment |
| matchType: "contains" as const, | ||
| isDefault: true, | ||
| isEnabled: true, | ||
| priority: 105, |
There was a problem hiding this comment.
你好,这个新规则的 priority 设置为 105,这会使它成为所有默认规则中优先级最低的一个。
根据 getActiveErrorRules 函数中的排序逻辑 (orderBy: [errorRules.priority, ...]),priority 值越小,优先级越高。
这个规则的匹配模式 "Missing or invalid 'alt' query parameter. Expected 'alt=sse'" 非常具体,应该在其他更通用的规则(例如 priority 为 60 的 "Missing required parameter" 规则)之前被匹配。
为了确保这个规则能被优先处理,建议将 priority 修改为一个更小的值,例如 59,使其优先级高于通用的参数错误规则。
| priority: 105, | |
| priority: 59, |
Greptile's behavior is changing!From now on, if a review finishes with no comments, we will not post an additional "statistics" comment to confirm that our review found nothing to comment on. However, you can confirm that we reviewed your changes in the status check section. This feature can be toggled off in your Code Review Settings by deselecting "Create a status check for each PR". |
There was a problem hiding this comment.
Pull request overview
This PR adds a new error rule to handle cases where service providers only support streaming Gemini requests. When a non-streaming generateContent request fails with the specific error message "Missing or invalid 'alt' query parameter. Expected 'alt=sse'", the system will return a 400 status code with a localized error message, allowing the Gemini client to detect that the non-streaming endpoint is unavailable and automatically fall back to using the streaming streamGenerateContent endpoint.
Key changes:
- Added a new error rule for Gemini non-streaming parameter errors with priority 105
- Configured custom error response in Gemini API format with localized Chinese message
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { | ||
| pattern: "Missing or invalid 'alt' query parameter. Expected 'alt=sse'", | ||
| category: "parameter_error", | ||
| description: "Not supported non-streaming request", | ||
| matchType: "contains" as const, | ||
| isDefault: true, | ||
| isEnabled: true, | ||
| priority: 105, | ||
| overrideResponse: { | ||
| error: { | ||
| code: 400, | ||
| status: "INVALID_ARGUMENT", | ||
| message: "当前中转站不支持 Gemini generateContent 端点", | ||
| }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
The overrideResponse field in this new error rule will not be synced properly when syncDefaultErrorRules is called. The syncDefaultErrorRules function (lines 797-808) only updates matchType, category, description, isEnabled, isDefault, and priority fields, but omits overrideResponse and overrideStatusCode. This means if this rule already exists in the database as a default rule, its overrideResponse won't be updated to the new value.
To fix this issue, the update statement in syncDefaultErrorRules should include overrideResponse and overrideStatusCode fields in the set() call.
| { | ||
| pattern: "Missing or invalid 'alt' query parameter. Expected 'alt=sse'", | ||
| category: "parameter_error", | ||
| description: "Not supported non-streaming request", | ||
| matchType: "contains" as const, | ||
| isDefault: true, | ||
| isEnabled: true, | ||
| priority: 105, | ||
| overrideResponse: { | ||
| error: { | ||
| code: 400, | ||
| status: "INVALID_ARGUMENT", | ||
| message: "当前中转站不支持 Gemini generateContent 端点", | ||
| }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
According to the PR description, this error rule should return a 400 HTTP status code so that Gemini can detect that generateContent is not available and automatically switch to streamGenerateContent. However, the current implementation only sets code: 400 inside the response body, but doesn't set overrideStatusCode: 400.
The code field in the Gemini error response is part of the JSON response body and doesn't control the actual HTTP status code. To ensure the HTTP response has a 400 status code, add overrideStatusCode: 400 to this error rule, like:
overrideStatusCode: 400,
This should be added after the priority field.
There was a problem hiding this comment.
Code Review Summary
This PR adds a new default error rule for Gemini non-streaming requests. The implementation is clean and correctly follows existing patterns.
PR Size: XS
- Lines changed: 16
- Files changed: 1
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Clean
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Adequate (default rules are covered by existing sync tests)
- Code clarity - Good
Validation Notes
-
Format: The
overrideResponsecorrectly follows the Gemini error format (GeminiErrorResponse) with required fieldserror.code(number),error.status(string), anderror.message(string). This will passvalidateErrorOverrideResponse(). -
Priority: Set to 105, appropriately higher than other rules to ensure early matching.
-
Pattern: Uses "contains" match type, which is appropriate for this error message pattern.
-
Note on
overrideStatusCode: This rule does not include an explicitoverrideStatusCode, meaning the HTTP status code will be passed through from the upstream response. Given that the upstream error "Missing or invalid 'alt' query parameter. Expected 'alt=sse'" typically returns a 400 status, this should correctly result in a 400 response to the client while providing a user-friendly Chinese message.
Automated review by Claude AI
There was a problem hiding this comment.
Code Review Summary
This PR adds a new default error rule for Gemini non-streaming requests. The implementation is clean and correctly follows existing patterns.
PR Size: XS
- Lines changed: 16
- Files changed: 1
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 0 | 0 |
| Simplification | 0 | 0 | 0 | 0 |
Review Coverage
- Logic and correctness - Clean
- Security (OWASP Top 10) - Clean
- Error handling - Clean
- Type safety - Clean
- Documentation accuracy - Clean
- Test coverage - Adequate (default rules are covered by existing sync tests)
- Code clarity - Good
Validation Notes
-
Format: The
overrideResponsecorrectly follows the Gemini error format (GeminiErrorResponse) with required fieldserror.code(number),error.status(string), anderror.message(string). This will passvalidateErrorOverrideResponse(). -
Priority: Set to 105, appropriately higher than other rules to ensure early matching.
-
Pattern: Uses "contains" match type, which is appropriate for this error message pattern.
-
Note on
overrideStatusCode: This rule does not include an explicitoverrideStatusCode, meaning the HTTP status code will be passed through from the upstream response. Given that the upstream error "Missing or invalid 'alt' query parameter. Expected 'alt=sse'" typically returns a 400 status, this should correctly result in a 400 response to the client while providing a user-friendly Chinese message.
Automated review by Claude AI
There was a problem hiding this comment.
Actionable comments posted: 1
Fix all issues with AI Agents 🤖
In @src/repository/error-rules.ts:
- Around line 293-308: The rule object matching pattern "Missing or invalid
'alt' query parameter. Expected 'alt=sse'" should explicitly set
overrideStatusCode: 400 and reduce its priority from 105 to a value in the 70-75
range (e.g., 70) so it aligns with other parameter_error rules and triggers
Gemini's HTTP-400-based fallback; update the rule object that contains fields
pattern, category, overrideResponse, isDefault, isEnabled, matchType and
priority to include overrideStatusCode: 400 and change priority to 70.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to Reviews > Disable Cache setting
📒 Files selected for processing (1)
src/repository/error-rules.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Use 2-space indentation in all code files
Files:
src/repository/error-rules.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use double quotes for strings instead of single quotes
Use trailing commas in multi-line structures
Enforce maximum line length of 100 characters
Use path alias@/*to reference files from./src/*directory
**/*.{ts,tsx,js,jsx}: Use Biome for linting and formatting with 2-space indent, double quotes, trailing commas, and 100 character max line length
Use path alias@/*to reference files in./src/*directory
Files:
src/repository/error-rules.ts
src/repository/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Repository pattern in
src/repository/to wrap Drizzle queries
Files:
src/repository/error-rules.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript strict mode for type safety
Use readonly or const assertions for immutable data structures
Files:
src/repository/error-rules.ts
src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.ts: Hash API keys using SHA-256 before storing in database, never store plaintext keys
Mask API keys and sensitive data in application logs
Validate required environment variables at startup with clear error messages
Files:
src/repository/error-rules.ts
src/{repository,actions}/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Avoid N+1 queries by using eager loading and batch queries for statistics
Files:
src/repository/error-rules.ts
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:01:39.354Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 539
File: src/types/user.ts:158-170
Timestamp: 2026-01-05T03:01:39.354Z
Learning: In TypeScript interfaces, explicitly document and enforce distinct meanings for null and undefined. Example: for numeric limits like limitTotalUsd, use 'number | null | undefined' when null signifies explicitly unlimited (e.g., matches DB schema or special UI logic) and undefined signifies 'inherit default'. This pattern should be consistently reflected in type definitions across related fields to preserve semantic clarity between database constraints and UI behavior.
Applied to files:
src/repository/error-rules.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: pr-review
- GitHub Check: Agent
- GitHub Check: Docker Build Test
- GitHub Check: 🌐 API Tests
- GitHub Check: pr-label
- GitHub Check: pr-description
| { | ||
| pattern: "Missing or invalid 'alt' query parameter. Expected 'alt=sse'", | ||
| category: "parameter_error", | ||
| description: "Not supported non-streaming request", | ||
| matchType: "contains" as const, | ||
| isDefault: true, | ||
| isEnabled: true, | ||
| priority: 105, | ||
| overrideResponse: { | ||
| error: { | ||
| code: 400, | ||
| status: "INVALID_ARGUMENT", | ||
| message: "当前中转站不支持 Gemini generateContent 端点" | ||
| } | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
git ls-files src/repository | head -20Repository: ding113/claude-code-hub
Length of output: 702
🏁 Script executed:
cat -n src/repository/error-rules.ts | head -50Repository: ding113/claude-code-hub
Length of output: 1791
🏁 Script executed:
wc -l src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 100
🏁 Script executed:
sed -n '280,320p' src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 1096
🏁 Script executed:
sed -n '1,100p' src/repository/error-rules.ts | grep -A 20 "DEFAULT_ERROR_RULES"Repository: ding113/claude-code-hub
Length of output: 49
🏁 Script executed:
grep -n "priority:" src/repository/error-rules.ts | head -20Repository: ding113/claude-code-hub
Length of output: 586
🏁 Script executed:
sed -n '310,370p' src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 1702
🏁 Script executed:
grep -n "overrideStatusCode" src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 519
🏁 Script executed:
sed -n '293,330p' src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 1164
🏁 Script executed:
sed -n '370,450p' src/repository/error-rules.ts | grep -A 15 "pattern:"Repository: ding113/claude-code-hub
Length of output: 2132
🏁 Script executed:
sed -n '400,550p' src/repository/error-rules.ts | grep -B 5 -A 15 "overrideStatusCode"Repository: ding113/claude-code-hub
Length of output: 49
🏁 Script executed:
sed -n '550,700p' src/repository/error-rules.ts | head -100Repository: ding113/claude-code-hub
Length of output: 2675
🏁 Script executed:
sed -n '290,750p' src/repository/error-rules.ts | grep -B 8 "overrideStatusCode"Repository: ding113/claude-code-hub
Length of output: 49
🏁 Script executed:
python3 << 'EOF'
import re
# Read the file
with open('src/repository/error-rules.ts', 'r', encoding='utf-8') as f:
content = f.read()
# Find the DEFAULT_ERROR_RULES array
match = re.search(r'const DEFAULT_ERROR_RULES = \[(.*)\];', content, re.DOTALL)
if match:
rules_content = match.group(1)
# Count how many rules have overrideStatusCode explicitly set
override_status_code_count = rules_content.count('overrideStatusCode:')
# Count total rules (each rule starts with a { on a new line)
rules = re.findall(r'\{[\s\S]*?\},', rules_content)
print(f"Total rules: {len(rules)}")
print(f"Rules with explicit overrideStatusCode: {override_status_code_count}")
print(f"\nFirst rule priority: {re.search(r'priority: (\d+)', rules_content).group(1)}")
# Check priorities in descending order
priorities = re.findall(r'priority: (\d+)', rules_content)
print(f"\nAll unique priorities (first 10): {sorted(set(priorities), reverse=True)[:10]}")
EOFRepository: ding113/claude-code-hub
Length of output: 153
🏁 Script executed:
grep -c "overrideStatusCode:" src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 68
🏁 Script executed:
grep "priority:" src/repository/error-rules.ts | sort -uRepository: ding113/claude-code-hub
Length of output: 582
🏁 Script executed:
sed -n '293,308p' src/repository/error-rules.tsRepository: ding113/claude-code-hub
Length of output: 503
🏁 Script executed:
grep -B 8 "category: \"parameter_error\"" src/repository/error-rules.ts | grep -E "(pattern:|priority:|category:)"Repository: ding113/claude-code-hub
Length of output: 280
🏁 Script executed:
sed -n '293,310p' src/repository/error-rules.ts | wc -LRepository: ding113/claude-code-hub
Length of output: 69
🏁 Script executed:
sed -n '293,800p' src/repository/error-rules.ts | grep -B 10 -A 5 "code: 400"Repository: ding113/claude-code-hub
Length of output: 503
🏁 Script executed:
sed -n '293,800p' src/repository/error-rules.ts | grep -B 10 "overrideStatusCode" | head -30Repository: ding113/claude-code-hub
Length of output: 49
🏁 Script executed:
sed -n '700,750p' src/repository/error-rules.ts | grep -B 5 -A 5 "overrideStatusCode:"Repository: ding113/claude-code-hub
Length of output: 49
🏁 Script executed:
sed -n '293,800p' src/repository/error-rules.ts | grep -E "(overrideStatusCode|priority:)" | head -30Repository: ding113/claude-code-hub
Length of output: 536
🏁 Script executed:
grep -A 20 "interface ErrorRule" src/repository/error-rules.ts | head -30Repository: ding113/claude-code-hub
Length of output: 564
🏁 Script executed:
rg "overrideStatusCode" -A 3 -B 3 src/ --type ts | head -60Repository: ding113/claude-code-hub
Length of output: 3426
🏁 Script executed:
sed -n '293,850p' src/repository/error-rules.ts | grep -B 15 "Missing required parameter" | grep -E "(pattern:|priority:|category:)"Repository: ding113/claude-code-hub
Length of output: 155
🏁 Script executed:
sed -n '200,850p' src/repository/error-rules.ts | grep -B 12 -A 2 "category.*parameter_error"Repository: ding113/claude-code-hub
Length of output: 932
🏁 Script executed:
sed -n '293,850p' src/repository/error-rules.ts | head -60Repository: ding113/claude-code-hub
Length of output: 1681
🏁 Script executed:
# Check how the overrideStatusCode field is used when null
grep -A 10 "overrideStatusCode.*null" src/ -r --type ts | head -40Repository: ding113/claude-code-hub
Length of output: 184
🏁 Script executed:
grep -r "overrideStatusCode" src/lib/ --include="*.ts" -B 2 -A 2 | head -50Repository: ding113/claude-code-hub
Length of output: 2779
🏁 Script executed:
# Check how error rules are applied in the middleware or error handler
find src -name "*.ts" -type f | xargs grep -l "overrideStatusCode" | head -5Repository: ding113/claude-code-hub
Length of output: 221
🏁 Script executed:
sed -n '1,150p' src/app/v1/_lib/proxy/error-handler.tsRepository: ding113/claude-code-hub
Length of output: 5331
🏁 Script executed:
grep -A 30 "overrideStatusCode" src/app/v1/_lib/proxy/error-handler.ts | head -50Repository: ding113/claude-code-hub
Length of output: 1794
补充状态码覆写和调整优先级以保持一致性。
新增规则的整体实现正确,建议进行以下调整:
-
建议显式设置
overrideStatusCode: 400:虽然上游返回 400 时,不设置此字段也能正确透传,但为了确保即使不同情况下也能保证返回 HTTP 400 以触发 Gemini 的自动切换机制,应显式设置此字段。 -
优先级应调整至 60-70 范围:当前优先级 105 高于所有规则(包括 prompt_limit 的 100),但同样分类的另一条 parameter_error 规则优先级仅为 71。建议将此规则的优先级调整至 70-75,与同分类规则保持一致,同时确保在 parameter 类错误中优先处理。
代码风格已符合所有编码规范(2 空格缩进、双引号、尾部逗号、行长 76 字符)。
🤖 Prompt for AI Agents
In @src/repository/error-rules.ts around lines 293-308, The rule object matching
pattern "Missing or invalid 'alt' query parameter. Expected 'alt=sse'" should
explicitly set overrideStatusCode: 400 and reduce its priority from 105 to a
value in the 70-75 range (e.g., 70) so it aligns with other parameter_error
rules and triggers Gemini's HTTP-400-based fallback; update the rule object that
contains fields pattern, category, overrideResponse, isDefault, isEnabled,
matchType and priority to include overrideStatusCode: 400 and change priority to
70.
* feat(error-rules): add Gemini non-streaming error rule 部分服务商仅支持流式的 gemini 请求, 这种情况下需要返回 400 状态码, 以便 gemini 检测到 generateContent 不可用时, 自动切换到 streamGenerateContent Signed-off-by: Kevin Cui <bh@bugs.cc> * fix: lint Signed-off-by: Kevin Cui <bh@bugs.cc> --------- Signed-off-by: Kevin Cui <bh@bugs.cc>
部分服务商仅支持流式的 gemini 请求, 这种情况下需要返回 400 状态码, 以便 gemini 检测到 generateContent 不可用时, 自动切换到 streamGenerateContent
Greptile Summary
This PR adds a new default error rule to handle Gemini API's non-streaming request limitation. The rule matches the error message "Missing or invalid 'alt' query parameter. Expected 'alt=sse'" and returns a 400 INVALID_ARGUMENT response, allowing clients to automatically fall back from the
generateContentendpoint tostreamGenerateContentfor providers that only support streaming.Key Changes:
Confidence Score: 5/5
Important Files Changed
Sequence Diagram
sequenceDiagram participant Client as Client participant ProxyServer as Proxy Server participant Gemini as Gemini API participant ErrorDetector as Error Detector participant Repo as Error Rules Repo Client->>ProxyServer: Request to /v1/messages with model=gemini-* ProxyServer->>Gemini: Forward request to generateContent endpoint Gemini->>Gemini: Check if non-streaming request Gemini-->>ProxyServer: 400 Error: "Missing or invalid 'alt' query parameter. Expected 'alt=sse'" ProxyServer->>ErrorDetector: Detect error pattern ErrorDetector->>Repo: Load active error rules (priority 105 rule first) Repo-->>ErrorDetector: Return Gemini non-streaming rule ErrorDetector->>ErrorDetector: Match pattern using "contains" method ErrorDetector-->>ProxyServer: Match found with override response ProxyServer->>ProxyServer: Apply override: Return 400 with Gemini error format ProxyServer-->>Client: 400 INVALID_ARGUMENT response Client->>Client: Auto-retry with streamGenerateContent Client->>ProxyServer: Retry request with stream=true ProxyServer->>Gemini: Forward to streamGenerateContent endpoint Gemini-->>ProxyServer: 200 Streaming response ProxyServer-->>Client: Stream response back to client