Skip to content

fix(catalog): normalize missing product prices - #2323

Merged
charlietlamb merged 1 commit into
mainfrom
charlie/normalize-product-response-main
Jul 20, 2026
Merged

fix(catalog): normalize missing product prices#2323
charlietlamb merged 1 commit into
mainfrom
charlie/normalize-product-response-main

Conversation

@charlietlamb

@charlietlamb charlietlamb commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Normalize missing product prices at catalog response boundaries so incomplete historical products cannot crash catalog analysis.


Summary by cubic

Normalize missing product prices to empty arrays in product and plan response mappers to prevent crashes when products lack price data. This stabilizes catalog analysis for historical or incomplete products.

  • Bug Fixes
    • Treat null/undefined product.prices as [] in getProductResponse and getPlanResponse.
    • Guard updateable check by iterating over (product.prices ?? []).

Written for commit 2d4fb04. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR adds ?? [] null-coalescing guards for product.prices at catalog response boundaries to prevent crashes when incomplete historical products have missing price data.

  • [Bug fixes] getPlanResponse.ts: guards product.prices before passing to mapToProductItems, preventing a crash when prices are absent on historical products.
  • [Bug fixes] getProductResponse.ts: guards product.prices for both the mapToProductItems call and the .some(isPrepaidPrice) check, but the upstream productToEffectivePrices call on line 141 still spreads product.prices directly and is not covered by this fix.

Confidence Score: 3/5

The fix is partially effective — it closes some crash paths but leaves productToEffectivePrices in getProductResponse unguarded for the same null-prices scenario.

Three of the four product.prices access sites are guarded, but productToEffectivePrices (called inside getProductProperties) spreads product.prices directly. Any incomplete historical product that reaches getProductResponse will still throw a TypeError at that spread before the guarded .some() check is ever reached.

getProductResponse.ts — the productToEffectivePrices call on line 141 needs the same null guard treatment as the other three sites.

Important Files Changed

Filename Overview
server/src/internal/products/productUtils/productResponseUtils/getPlanResponse.ts Adds ?? [] guard for product.prices before passing to mapToProductItems — safe and correct.
server/src/internal/products/productUtils/productResponseUtils/getProductResponse.ts Adds ?? [] guards for mapToProductItems and .some(...), but the call to productToEffectivePrices on line 141 spreads product.prices directly and remains unguarded — still crashable for null-prices products.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[FullProduct with null prices] --> B{getProductResponse}
    B --> C["mapToProductItems\n(product.prices ?? []) ✅"]
    B --> D["getProductProperties"]
    D --> E["productToEffectivePrices\n(...product.prices) ❌ unguarded"]
    D --> F["(product.prices ?? []).some(...) ✅"]
    B --> G["getAttachScenario"]

    H[FullProduct with null prices] --> I{getPlanResponse}
    I --> J["mapToProductItems\n(product.prices ?? []) ✅"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[FullProduct with null prices] --> B{getProductResponse}
    B --> C["mapToProductItems\n(product.prices ?? []) ✅"]
    B --> D["getProductProperties"]
    D --> E["productToEffectivePrices\n(...product.prices) ❌ unguarded"]
    D --> F["(product.prices ?? []).some(...) ✅"]
    B --> G["getAttachScenario"]

    H[FullProduct with null prices] --> I{getPlanResponse}
    I --> J["mapToProductItems\n(product.prices ?? []) ✅"]
Loading

Comments Outside Diff (1)

  1. server/src/internal/products/productUtils/productResponseUtils/getProductResponse.ts, line 141 (link)

    P1 Incomplete null guard — productToEffectivePrices still spreads product.prices directly

    The same "incomplete historical products" this PR targets will crash here. productToEffectivePrices (in shared/utils/productUtils/convertProduct/productToEffectivePrices.ts) is implemented as [...product.prices, ...], so if product.prices is null/undefined at runtime it throws a TypeError: null is not iterable. The three ?? [] guards added in this PR protect mapToProductItems and .some(...) but leave this call unguarded.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: server/src/internal/products/productUtils/productResponseUtils/getProductResponse.ts
    Line: 141
    
    Comment:
    **Incomplete null guard — `productToEffectivePrices` still spreads `product.prices` directly**
    
    The same "incomplete historical products" this PR targets will crash here. `productToEffectivePrices` (in `shared/utils/productUtils/convertProduct/productToEffectivePrices.ts`) is implemented as `[...product.prices, ...]`, so if `product.prices` is `null`/`undefined` at runtime it throws a `TypeError: null is not iterable`. The three `?? []` guards added in this PR protect `mapToProductItems` and `.some(...)` but leave this call unguarded.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
server/src/internal/products/productUtils/productResponseUtils/getProductResponse.ts:141
**Incomplete null guard — `productToEffectivePrices` still spreads `product.prices` directly**

The same "incomplete historical products" this PR targets will crash here. `productToEffectivePrices` (in `shared/utils/productUtils/convertProduct/productToEffectivePrices.ts`) is implemented as `[...product.prices, ...]`, so if `product.prices` is `null`/`undefined` at runtime it throws a `TypeError: null is not iterable`. The three `?? []` guards added in this PR protect `mapToProductItems` and `.some(...)` but leave this call unguarded.

Reviews (1): Last reviewed commit: "fix(catalog): normalize missing product ..." | Re-trigger Greptile

@capy-ai

capy-ai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@charlietlamb
charlietlamb merged commit db80bf2 into main Jul 20, 2026
19 of 20 checks passed
@charlietlamb
charlietlamb deleted the charlie/normalize-product-response-main branch July 20, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant