doc-site
Runbooks

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.js

Compare 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-gifts

Reload, 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 seeWhat it means
No events at allThe mini-cart element never initialised. Check for an earlier console error and confirm the bundle loaded
start but no successThe network call failed. Check the network panel for the /cart/ request
success but no cart:changedAn exception in a listener stopped the chain. Look for the error above it
cart:changed but no debouncedThe root element is not running its debounce. Confirm one mini-cart element exists, not two
Every event twiceTwo 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-gifts

The 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

  1. Reload in a private window with an empty cart.
  2. Add a product. Confirm the drawer opens and the count is right.
  3. Change the quantity twice quickly. Confirm one debounced render, not two.
  4. Cross a reward threshold. Confirm the bar fills and the gift appears.
  5. Remove the item. Confirm the gift is removed with it.
  6. 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.

On this page