Corrective Action: CartContext TypeError on Every Page Load
Date: 2026-04-03 Category: Data Format Mismatch Impact: Every page on valuefirstteam.com fired a
TypeError: Cannot read properties of undefined (reading 'reduce')in the browser console. Cart functionality silently broken site-wide. Resolution Time: ~15 minutes from Mirror audit detection to committed fix
Incident
What Happened
Mirror's visual QA audit of 10 episode pages reported a CartContext TypeError firing twice on every page load across the entire site. The error was Cannot read properties of undefined (reading 'reduce'), originating from the CartProvider component that wraps every page.
The cart totals recalculation effect was calling calculateCartTotals(cart.items, cart.promoCode) — passing two positional arguments. But the function signature had been refactored to accept a single object { items, state, country }. The array cart.items was being destructured as the input object, producing items: undefined, which then called .reduce() on undefined.
Timeline
| Time | Event |
|---|---|
| Unknown | calculateCartTotals refactored from (items, promoCode) to ({ items, state, country }) |
| Unknown | CartContext.tsx line 137 NOT updated to match new signature |
| 2026-04-03 ~18:00 CT | Mirror audit detects TypeError on all 10 tested pages |
| 2026-04-03 ~18:15 CT | Root cause identified: function signature mismatch |
| 2026-04-03 ~18:20 CT | Fix applied: switched to calculateCartTotalsLegacy which preserves old signature |
| 2026-04-03 ~18:45 CT | Type-check clean, committed (207b9489), pushed, deploying |
Root Cause
The calculateCartTotals function in src/lib/cart/utils.ts was refactored to accept a single object parameter with location-based tax calculation:
// NEW signature (utils.ts line 153)
export function calculateCartTotals(input: CartTotalsInput): CartTotalsResult {
const { items, state, country } = input;
A backward-compatible calculateCartTotalsLegacy function was created (line 171) that preserved the old (items, promoCode, taxRate) signature. But the caller in CartContext.tsx was never updated:
// CartContext.tsx line 137 — BROKEN
const totals = calculateCartTotals(cart.items, cart.promoCode);
When cart.items (an array) is passed as input, destructuring { items } from an array yields undefined. The subsequent calculateSubtotal(items) calls items.reduce() on undefined.
TypeScript should have caught this at build time, but the mismatch between an array and an object type may have been masked by any types or the build may not have been re-run after the refactor.
Category: Data Format Mismatch
Function signature changed without updating all callers. The legacy compatibility function existed but wasn't wired in.
Fix Applied
Immediate Resolution
Changed CartContext.tsx to import and call calculateCartTotalsLegacy instead of calculateCartTotals.
Code/Configuration Changes
| File | Change |
|---|---|
apps/website/src/components/cart/CartContext.tsx line 12 |
Import calculateCartTotalsLegacy instead of calculateCartTotals |
apps/website/src/components/cart/CartContext.tsx line 137 |
Call calculateCartTotalsLegacy(cart.items, cart.promoCode) |
Verification
npx tsc --noEmit— clean (no new errors)- Commit
207b9489pushed to main, Vercel deploying
Prevention Measures
Rules Added
| Layer | File | Rule |
|---|---|---|
| Critical Lessons | MEMORY.md |
When refactoring a function signature, grep for ALL callers and update them. If creating a legacy compatibility function, wire existing callers to it immediately. |
Detection Triggers
When refactoring any utility function's parameter shape, run tsc --noEmit before committing to catch type mismatches at compile time.
Lessons
When a function signature is refactored and a backward-compatible version is created, the existing callers must be updated in the same commit. Creating calculateCartTotalsLegacy acknowledged that the old signature was in use — but the caller was left on the new function with the old arguments. The legacy function existed precisely for this case and wasn't connected.
This is a variant of the "create but don't wire" pattern: building the right solution but not connecting it to the code that needs it.
Related Incidents
- Unicode escape double-encoding (2026-03-16): Build pipeline processed source differently than expected, producing user-visible errors that weren't caught until production audit.
- Portal Documents filter (2026-03-02):
listing_content_typevalue mismatch made documents invisible. Same pattern — data format assumption without verification.