Cart and line item errors
Use this when a quantity change fails, the drawer shows a cart that does not match checkout, or a line item's properties look wrong.
Diagnose
1. Turn on cart logging and reproduce the problem.
window.enableLogging('cart-event-bus,cart-store,cart-fetch-interceptor,cart-command-controller');Reload, then repeat the action. The bus logs every event it emits and every listener it binds, which tells you whether the failure happens before or after the request.
2. Read the cart itself, not the drawer.
await fetch('/cart.js').then((r) => r.json());If this matches checkout and the drawer does not, the drawer missed an event. If this is also wrong, the cause is outside the theme.
3. Find the request in the network tab. Filter for cart, and note the endpoint, the status and the response.
| What you see | What it means |
|---|---|
400 no valid id or line parameter | A stale line item key. Continue below |
| No request at all | The element never dispatched, or the command controller is not listening |
| A request the drawer did not react to | Another writer the interceptor did not catch |
422 | Shopify rejected the change, usually because of inventory |
4. For a stale key, check for duplicate variants.
const cart = await fetch('/cart.js').then((r) => r.json());
cart.items.map((i) => [i.key, i.variant_id, i.quantity, i.properties]);If two lines share a variant ID, resolveLineItem cannot tell which line was meant and does not guess. This happens
most often when a free gift is the same product as a purchased line.
5. Check which code added the line. Every add from theme code should carry a _source property:
cart.items.map((i) => [i.title, i.properties?.['_source']]);A line with no _source came from code that does not use withCartSource, which usually means a third-party app.
Repair
Stale line item key, one matching variant. resolveLineItem should already recover from this. If it did not, the
caller sends the key straight to the cart API instead of going through SimpleCartService. Route the call through the
service.
Stale line item key, duplicate variants. resolveLineItem refuses this case on purpose, so do not patch it there.
Change the caller to read the cart again and render, instead of reusing a key it stored earlier.
The drawer missed a third-party write. Compare the request's method and URL with the interceptor's matcher, which
only handles a POST to a URL matching /cart/*.js. An app that writes another way needs its own handler, or a
mini-cart:get dispatch after it acts.
Properties keep changing. The line item properties update chain does this on purpose. promotionLabelQuantityRule
clears the Promotion property when quantity drops below 3, and remainingAmountRecalculationRule rewrites remaining
amounts on lines whose vendor contains medispa. If a rule fires when it should not, fix the rule's condition, not the
caller.
The drawer reacts twice to its own write. The response is missing the x-source: mini-cart header, so the
interceptor treats the theme's own request as an outside write.
Verify
- In a private window, add a product, change its quantity up and down, and remove it.
- Confirm
/cart.jsand the drawer agree after each step. - Add a product that triggers a gift, then change the original line's quantity. This tests the duplicate-variant case.
- Apply and remove a discount code, and confirm the progress bar updates.
- Go through to checkout and confirm the line items and any deposit balances are right.
Escalate
If /cart.js is wrong, and not only the drawer, the theme did not cause it. Use the _source property to find which
app wrote to the cart, then check that app's settings. For an order already placed, the order record in Shopify admin
shows the final line item properties.