fix: return proper type in calculateAndUpdateRequestCost early exit#915
Merged
fix: return proper type in calculateAndUpdateRequestCost early exit#915
Conversation
The early return when no price data is found was returning undefined instead of the expected object type. Fixed to return consistent object structure matching other early returns in the function. Fixed: - Type 'undefined' is not assignable to type return object in response-handler.ts:3111 CI Run: https://github.com/ding113/claude-code-hub/actions/runs/23082651541 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ding113
added a commit
that referenced
this pull request
Mar 14, 2026
* fix(proxy): prevent TypeError in NON_RETRYABLE_CLIENT_ERROR handler with transport errors Fixes a regression where native transport errors (SocketError, UND_ERR_SOCKET) could be misclassified as NON_RETRYABLE_CLIENT_ERROR by error rule matching, causing the error handler to crash with "TypeError: getDetailedErrorMessage is not a function" instead of properly rethrowing the original error. Changes: 1. errors.ts: Add transport-error guard in categorizeErrorAsync() at priority 1.5 - Detects SocketError, undici error codes (UND_ERR_SOCKET, ECONNREFUSED, etc.) - Returns SYSTEM_ERROR before error rule matching to prevent misclassification - Preserves correct failover behavior for network-level failures 2. forwarder.ts: Bifurcate NON_RETRYABLE_CLIENT_ERROR branch on instanceof check - ProxyError path: uses full ProxyError fields (statusCode, upstreamError, etc.) - Plain Error path: uses only base Error fields, avoids calling non-existent methods - Both paths preserve original error rethrow and provider chain recording 3. Add regression tests: - Test A: Verifies plain SocketError + forced NON_RETRYABLE_CLIENT_ERROR doesn't throw TypeError - Test B: Verifies categorizeErrorAsync returns SYSTEM_ERROR for transport errors All 657 proxy unit tests pass. Type check and lint pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: format code (dev-a3fd666) * fix: assorted UI/UX and logic small fixes * feat(request-filters): advanced mode operations and execution engine (#912) * feat(request-filters): add advanced mode operations and final execution phase Introduce two new axes for request filters: rule mode (simple/advanced) and execution phase (guard/final). Advanced mode supports a JSON-defined operation DSL with set, remove, merge, and insert operations. The final phase executes after all provider overrides, enabling post-processing use cases like system message injection with dedup and cache_control manipulation. Key changes: - Schema: add rule_mode, execution_phase, operations columns + index - Types: new FilterOperation DSL (SetOp, RemoveOp, MergeOp, InsertOp) - Engine: 4-bucket cache split, applyFinal() method, operation executors with deep merge (null-as-delete), deep equal dedup, anchor matching - Forwarder: integrate applyFinal in standard and Gemini branches - Validation: validateOperations with ReDoS prevention via safe-regex - UI: rule mode toggle, execution phase selector, JSON operations editor - i18n: new keys for all 5 locales (en, zh-CN, zh-TW, ja, ru) - Tests: 29 new tests covering all operation types and edge cases * chore: format code (feat-request-filter-advanced-mode-ccee157) * fix(security): harden request filter engine against prototype pollution and ReDoS bypass - Block __proto__/constructor/prototype traversal in parsePath, deepMerge, and validation - Add runtime safe-regex check in matchElement for regex matchers - Fix ReDoS bypass in updateRequestFilterAction by checking effective target/matchType/action - Add operations array length limit (max 50) and value validation for set/merge/insert ops - Fix UI default executionPhase to match server default (guard, not final) - Fix parsePath regex corruption from edit tool (double-escaped backslashes) * fix(i18n,a11y): address code review findings for request filters - Remove console.error from filter-dialog.tsx (error already shown via toast) - Fix ja table.actions translation to "操作" - Fix ru table.createdAt to "Дата создания" - Fix zh-CN confirmDelete halfwidth ? to fullwidth ? - Show operations count in target column for advanced mode filters - Add aria-label to filter toggle Switch for accessibility * fix(request-filters): address CodeRabbit review findings across 8 issues Security: - parsePath now rejects entire path on unsafe key instead of silently dropping segments (e.g. `a.__proto__.b` no longer becomes `a.b`) - getValueByPath returns undefined on rejected paths instead of root object - MergeOp/InsertOp types narrowed to scope:"body" with runtime validation Correctness: - GET/HEAD requests now run final-phase filters for header-only operations (both Gemini and non-Gemini branches) - Gemini branch clones body via structuredClone before applyFinal to prevent in-place mutation of session.request.message on retries - Reject advanced+guard combo at validation layer (create + update paths) Quality: - Consolidate 3 redundant getRequestFilterById calls into 1 per update - Fix Switch aria-label to describe toggle purpose with filter name - Add $type<FilterOperation[]|null> annotation to operations schema column - Add toggleStatus i18n key across all 5 locales --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix(proxy): distinguish local client abort 499 from upstream HTTP 499 Adds isLocalAbort flag to ProxyError to differentiate CCH-synthesized 499 errors (client disconnection) from upstream providers returning HTTP 499. Previously, isClientAbortError() matched any ProxyError with statusCode 499, causing upstream 499 responses to bypass circuit-breaker and fallback logic. Now only ProxyError instances explicitly marked with isLocalAbort=true are treated as client aborts. Also removes the broad "aborted" substring match from the message whitelist to avoid false positives on upstream error messages containing that word. * chore: format code (dev-bcdc6b7) * fix: speed up users last usage lookup * fix(db): add covering index for getUsers LATERAL join and fix CASE type mismatch in cache-hit-rate-alert 1. Add idx_usage_ledger_key_created_at_desc_cover index on (key, created_at DESC NULLS LAST, final_provider_id) with partial filter WHERE blocked_by IS NULL. This enables index-only scans for the LATERAL last-usage-per-key subquery in getUsers, reducing query time from ~28s to ~6.4ms. 2. Fix "CASE types integer and text cannot be matched" error in cache-hit-rate-alert by adding explicit ::integer casts to the ttlFallbackSecondsExpr CASE branches. PostgreSQL inferred parameterized values as text, which conflicted with integer literals (3600, 300) in the outer ttlSecondsExpr CASE. 3. Add regression test verifying ::integer casts are present in the generated SQL for cache-hit-rate-alert queries. * chore: format code (dev-63c4c9c) * feat(proxy): support codex desktop alias matching * feat(billing): remove 1M context premium and add Codex 272k badge Anthropic has GA'd the 1M context window for Opus 4.6 and Sonnet 4.6, eliminating the long-context surcharge. The beta header `context-1m-2025-08-07` is no longer needed. Backend: - Remove CONTEXT_1M_SUPPORTED_MODEL_PREFIXES, CONTEXT_1M_BETA_HEADER, Context1mPreference type, isContext1mSupportedModel, shouldApplyContext1m - Remove AnthropicContext1mHeaderOverrideSpecialSetting type and derived logic - Simplify forwarder 1M decision: just record client header, no injection - Remove provider-selector Step 2.5 context1mPreference filter - Add Codex 272k badge: set context1mApplied when input > 272k tokens Frontend: - Remove 1M Context Window setting from provider forms (options-section, provider-form-context, provider-form-types, batch-edit, legacy form) - Remove premium pricing tooltips from usage-logs-table i18n: - Remove context1m provider config keys (5 languages) - Remove context1mPricing premium text from dashboard (5 languages) Tests: - Remove anthropic_context_1m_header_override test cases - Remove context1mPreference UI and patch draft tests * chore: format code (dev-5a7db40) * feat(usage): add anthropic effort tracking and display. #900 (#901) * feat(usage): add anthropic effort tracking and display. #900 * chore: format code (effort-tag-558c8fd) --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * feat(deploy): support incremental updates to preserve secrets on re-deploy Previously, re-running deploy.sh/deploy.ps1 would regenerate DB_PASSWORD, ADMIN_TOKEN, and SUFFIX, causing database connection failures because the PostgreSQL data volume still held the old password. Now the scripts auto-detect existing deployments (.env + docker-compose.yaml) and enter update mode: preserving DB_PASSWORD, ADMIN_TOKEN, SUFFIX, APP_PORT, and any user-added custom env vars (e.g. LANGFUSE_*, FETCH_*). A --force-new / -ForceNew flag allows forcing a fresh install when needed. * feat: support long-context pricing metadata * feat: support long-context pricing metadata * fix: return billing metadata when pricing is missing * refactor: extract utilities, i18n fixes, and codex threshold constant - Extract getUserFacingProviderTypes() to filter internal provider types - Extract CODEX_1M_CONTEXT_TOKEN_THRESHOLD constant and maybeSetCodexContext1m helper - Refactor limit-rule-picker to use t() shorthand for getTranslation - i18n: add templateClaudeApi/GeminiApi/OpenaiApi keys to errorRules (5 locales) - i18n: replace hardcoded API names in override-section with t() calls - Add codex desktop alias matching tests for detectClientFull - Improve error logging in edit-key-form provider group fetch - Remove default parameter value from data-table actions column title * refactor: move effort badge from table to request detail dialog Move the Anthropic effort badge display from the usage logs table (both admin and my-usage) into the SummaryTab's Session Info section within the ErrorDetailsDialog. This declutters the table view and provides richer context by showing override information (original vs overridden effort) when a provider parameter override changes the effort value. - Add extractAnthropicEffortInfo utility combining anthropic_effort and provider_parameter_override special settings - Display effort badge(s) in SummaryTab with override arrow notation - Remove effort badge from ModelDisplayWithRedirect, both usage tables - Add i18n keys (effort.label, effort.overridden) for all 5 languages - Add 11 table-driven unit tests covering all branches * fix: return proper object type in updateRequestCostFromUsage (#914) Fixed TypeScript error where bare 'return;' returned undefined instead of the expected object type with costUsd, resolvedPricing, longContextPricing, and longContextPricingApplied properties. Error: src/app/v1/_lib/proxy/response-handler.ts(3111,7): error TS2322: Type 'undefined' is not assignable to type '{ costUsd: string | null; resolvedPricing: ResolvedPricing | null; longContextPricing: ResolvedLongContextPricing | null; longContextPricingApplied: boolean; }'. CI Run: https://github.com/ding113/claude-code-hub/actions/runs/23082652215 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: return proper type in calculateAndUpdateRequestCost early exit (#915) The early return when no price data is found was returning undefined instead of the expected object type. Fixed to return consistent object structure matching other early returns in the function. Fixed: - Type 'undefined' is not assignable to type return object in response-handler.ts:3111 CI Run: https://github.com/ding113/claude-code-hub/actions/runs/23082651541 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com> Co-authored-by: Night <lzy200816@gmail.com> Co-authored-by: Hwwwww-dev <47653238+Hwwwww-dev@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CI Auto-Fix
Original PR: #913
Failed CI Run: Non-Main Branch CI/CD
Error Fixed
src/app/v1/_lib/proxy/response-handler.ts:3111return;to return proper object structureAnalysis
The function
calculateAndUpdateRequestCosthas a return type of:The early return at line 3111 was using a bare
return;which returnsundefined, but the function expects an object. The fix returns the same structure as other early returns in the function (lines 3058-3063, 3050-3053, 3168-3173).Verification
bun run typecheckpassesAuto-generated by Claude AI
Greptile Summary
This PR fixes a type error in
updateRequestCostFromUsagewhere a barereturn;on the "no price data found" code path returnedundefinedinstead of the expected{ costUsd, resolvedPricing, longContextPricing, longContextPricingApplied }object. All three call sites (lines 1001, 2016, 3251) immediately accesscostUpdateResult.longContextPricingApplied, so the bare return would have caused a runtimeTypeErrorwhen no pricing data was available for a model. The fix returns the same null-valued object structure used by the other early-return paths in the function.return;→ proper object return in the "missing price data" branch ofupdateRequestCostFromUsagenull/false) match the semantics of all other early exits in the functionConfidence Score: 5/5
return;with the correct object return type. It exactly matches the pattern of all other early returns in the same function. There are no logic changes, no new dependencies, and it fixes a real runtime crash path.Important Files Changed
return;that returnedundefinedinstead of the expected object, which would cause a runtime TypeError at all three call sites that immediately access.longContextPricingAppliedon the result.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[updateRequestCostFromUsage called] --> B{usage exists?} B -- No --> R1["return { costUsd: null, ... }"] B -- Yes --> C{model available?} C -- No --> R2["return { costUsd: null, ... }"] C -- Yes --> D[Resolve pricing from DB] D --> E{priceData valid?} E -- "No (BUG FIX HERE)" --> F["return { costUsd: null, ... }"] E -- Yes --> G[Calculate cost] G --> H{cost > 0?} H -- Yes --> R3["return { costUsd: cost, resolvedPricing, ... }"] H -- No --> R4["return { costUsd: null, resolvedPricing, ... }"] style F fill:#90EE90,stroke:#333,stroke-width:2pxLast reviewed commit: 35848b7