Skip to content

fix: return proper type in calculateAndUpdateRequestCost early exit#915

Merged
ding113 merged 1 commit intodevfrom
claude-fix-pr-913-23082651541
Mar 14, 2026
Merged

fix: return proper type in calculateAndUpdateRequestCost early exit#915
ding113 merged 1 commit intodevfrom
claude-fix-pr-913-23082651541

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 14, 2026

CI Auto-Fix

Original PR: #913
Failed CI Run: Non-Main Branch CI/CD

Error Fixed

File Error Fix
src/app/v1/_lib/proxy/response-handler.ts:3111 Type 'undefined' is not assignable to type return object Changed bare return; to return proper object structure

Analysis

The function calculateAndUpdateRequestCost has a return type of:

{
  costUsd: string | null;
  resolvedPricing: ResolvedPricing | null;
  longContextPricing: ResolvedLongContextPricing | null;
  longContextPricingApplied: boolean;
}

The early return at line 3111 was using a bare return; which returns undefined, 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 typecheck passes
  • No logic changes made - only fixed the return type
  • Change is minimal and targeted

Auto-generated by Claude AI

Greptile Summary

This PR fixes a type error in updateRequestCostFromUsage where a bare return; on the "no price data found" code path returned undefined instead of the expected { costUsd, resolvedPricing, longContextPricing, longContextPricingApplied } object. All three call sites (lines 1001, 2016, 3251) immediately access costUpdateResult.longContextPricingApplied, so the bare return would have caused a runtime TypeError when 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.

  • Bug fixed: bare return; → proper object return in the "missing price data" branch of updateRequestCostFromUsage
  • No logic changes: the returned values (null / false) match the semantics of all other early exits in the function
  • Impact: prevents a runtime crash when a model has no price data configured

Confidence Score: 5/5

  • This PR is safe to merge — it fixes a clear type/runtime bug with a minimal, correct change.
  • The change is a one-line-to-five-line fix that replaces a bare 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.
  • No files require special attention.

Important Files Changed

Filename Overview
src/app/v1/_lib/proxy/response-handler.ts Fixes a bare return; that returned undefined instead of the expected object, which would cause a runtime TypeError at all three call sites that immediately access .longContextPricingApplied on 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:2px
Loading

Last reviewed commit: 35848b7

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 ding113 merged commit 8ca7e60 into dev Mar 14, 2026
1 check passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Mar 14, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant