Cart total or gift is wrong
The drawer disagrees with the cart, a free gift is missing, or a cart write fails with a 400.
Cart total or gift is wrong
Use this runbook when the drawer total disagrees with the cart page, the reward progress bar does not move, a free gift
is missing or duplicated, or a cart action fails with 400 no valid id or line parameter.
The cart drawer redraws from events. Anything that breaks the event flow leaves correct data in Shopify and stale data on screen, so start by separating the two.
Diagnose
1. Check what Shopify holds
Open the cart JSON directly:
https://beautyaffairs.com.au/cart.jsCompare the item count, subtotal and line properties against what the drawer shows.
If Shopify is correct and the drawer is wrong, this is a rendering or event problem. Continue.
If Shopify itself is wrong, the problem is whatever wrote to the cart, not the drawer. Note the _source property on
the wrong line, which records where the add came from, and find the code that sets that value.
2. Turn on cart logging
https://beautyaffairs.com.au/?ba_debug=mini-cart,cart-service,cart-store,cart-event-bus,cart-fetch-interceptor,mini-cart:free-giftsReload, then change a quantity and watch the console.
A healthy sequence for a quantity change is cart:change:start, then cart:change:success, then cart:changed, then
cart:changed:debounced.
3. Read the gap in that sequence
| What you see | What it means |
|---|---|
| No events at all | The mini-cart element never initialised. Check for an earlier console error and confirm the bundle loaded |
start but no success | The network call failed. Check the network panel for the /cart/ request |
success but no cart:changed | An exception in a listener stopped the chain. Look for the error above it |
cart:changed but no debounced | The root element is not running its debounce. Confirm one mini-cart element exists, not two |
| Every event twice | Two interceptors, or a request that copied the X-Source header |
4. Check for a stale line item key
A 400 no valid id or line parameter in the network panel means the line item key the drawer used no longer exists.
Shopify hashes a line's properties into its key, so any property write replaces the key while the line itself is unchanged. The drawer recovers by matching on variant id, but only when exactly one line matches that variant.
Check /cart.js for two lines sharing a variant id. A purchased item plus its free gift is the usual pair, and by
design the drawer does not guess between them.
5. Check free gift eligibility
?ba_debug=mini-cart:free-giftsThe log shows the cart total it computed and which gifts it matched.
Confirm the gift metaobject's price_threshold matches a tier's Price Threshold setting exactly. The progress bar
attaches a gift to a tier by comparing those two numbers, and a one-dollar mismatch detaches it without an error.
Confirm the cart total the log reports excludes free gift lines. Lines carrying __free_gift are not counted towards
the next tier.
6. Check for a hidden progress bar
The progress bar hides when the cart contains a line whose vendor name contains medispa. A missing progress bar in a
cart containing a treatment is correct behaviour.
Repair
The drawer is stale but Shopify is right
Reload the page. This confirms the diagnosis rather than fixing anything, because the drawer rebuilds from /cart.js on
load.
If the state is correct after a reload and wrong again after the next change, an event is being lost. Find which listener threw, from step 3.
A third-party app writes without the drawer noticing
Confirm the app's request appears in the network panel and that the interceptor logged it.
The interceptor matches URLs against /cart(.js|/*.js) and skips any response carrying X-Source: mini-cart, so it
misses an app request that copied that header. Check the request headers in the network panel.
Cart hangs after one action
This is a deadlock. A task inside CartService.runExclusive called a SimpleCartService method, which re-enters the
queue and waits for itself.
Find that call in recently changed cart code. Tasks inside runExclusive may call only the low-level methods on
CartService.
The gift is wrong
Fix the metaobject rather than the code. Instructions are in Cart rewards and free gifts.
Everything fires twice
Look for a second mini-cart element in the DOM, or a second section rendering mini-cart/elements/mini-cart.ts. Each
root element creates its own interceptor.
document.querySelectorAll('mini-cart').length;Anything other than 1 explains the duplication.
Verify
- Reload in a private window with an empty cart.
- Add a product. Confirm the drawer opens and the count is right.
- Change the quantity twice quickly. Confirm one debounced render, not two.
- Cross a reward threshold. Confirm the bar fills and the gift appears.
- Remove the item. Confirm the gift is removed with it.
- Reload and confirm the drawer matches
/cart.js.
Escalate
A discount that displays wrongly but charges correctly at checkout belongs to Discount Ninja.
A total that is wrong in Shopify's own cart as well as in the drawer belongs to Shopify or to the app that wrote the
line. The _source property on the line shows which code added it.
Background is in Mini-cart.