Cart items intermittently dropping after add-to-cart (custom theme / AJAX cart)

Cart items intermittently dropping after add-to-cart (custom theme / AJAX cart)

Link to Shopify Store

Reproduction steps

  1. Add to cart
  2. Navigate to another page
  3. Cart has been dropped
  4. refresh page cart is still dropped

Other times, the badge count updates incorrectly or lags behind.

  1. Add to cart
  2. Navigate to another page
  3. Cart has been dropped
  4. refresh page cart icon shows item correctly added

Additional info

We’ve been experiencing an intermittent issue on our Shopify Plus store where items added to cart sometimes disappear immediately or fail to appear in the cart drawer after clicking “Add to Cart.”

Setup details:

Shopify Plus

Custom theme with an AJAX cart drawer

initAdd() function posts directly to the product form action (using XMLHttpRequest + HTML parsing)

We’ve previously used preorder apps (Stoq / PreorderMe) which injected their own cart scripts, but they’re now disabled

Cart count is updated by parsing .js-cart-count from the AJAX response rather than from /cart.js

Error logs occasionally show 409 Conflict responses when the issue occurs

What we’ve tried:

Disabling preorder apps and clearing theme cache

Testing with Stoq app disabled — issue still occurs

Comparing legacy cart code vs. newer version (both rely on HTML replacement from /cart endpoint)

Shopify Support confirmed no backend/server errors (not a 500-level issue)

What type of topic is this

Troubleshooting

Hey @Dow_Dodds :waving_hand:, happy to look into this, if you’re able, could you share the full details of the 409 Conflict responses, including the exact endpoint and method, the request payload, the complete response body, timestamps, and headers like X-Request-ID?

If you’re also able to share the full initAdd() function and how it is bound to the button or form that would be super helpful as well. A short HAR file or DevTools Network waterfall around the error would be very helpful too if you can share that.

Steps hereto capture one in Chrome:

https://support.google.com/admanager/answer/10358597

For pulling the 409 error header info, there’s some steps on basic console usage and capturing logs in Chrome DevTools, here in this guide if you need it:

https://developer.chrome.com/docs/devtools/console

Hope to hear from you soon, let me know if I can clarify anything on my end here.

Hey Alan,

Thanks for picking this up.

This is the current InitAdd function:

initAdd:function(){
if (document.getElementsByClassName(‘scope-product-form’).length !== 0) {
document.getElementsByClassName(‘scope-product-form’)[0].getElementsByTagName(‘form’)[0].addEventListener(‘submit’, function(_evt){
Cart.currentTotal = parseInt(document.getElementsByClassName(‘js-cart-count’)[0].innerHTML)
_evt.preventDefault();
let _data = new FormData(this);
let _object = {};
_data.forEach(function(_value, _key){
_object[_key] = _value;
});
let _json = JSON.stringify(_object);

let _xhr = new XMLHttpRequest();
_xhr.onreadystatechange = function() {

if (_xhr.readyState === 4) {

let _temp = document.createElement(“DIV”);
_temp.innerHTML = _xhr.responseText;

if (_temp.getElementsByClassName(‘c-cart__wrapper’).length !== 0 && document.getElementsByClassName(‘c-cart__wrapper’).length !== 0) {
document.getElementsByClassName(‘c-cart__wrapper’)[0].innerHTML = _temp.getElementsByClassName(‘c-cart__wrapper’)[0].innerHTML;
Cart.initQtyUpdate();
Cart.doToggle();
} else if (_temp.getElementsByClassName(‘c-cart’).length !== 0 && document.getElementsByClassName(‘c-cart’).length !== 0) {
document.getElementsByClassName(‘c-cart’)[0].innerHTML = _temp.getElementsByClassName(‘c-cart’)[0].innerHTML;
}

Resize.doResize();
let _success = document.getElementById(‘js-product-title’).innerHTML + ’ added to your cart’;
document.getElementById(‘js-cart-notification’).innerHTML = _success;

let _qtyEls = document.getElementsByClassName(‘c-cart__item__form__qty’);
if (_qtyEls.length > 0) {
for (let _k=0;_k<_qtyEls.length;_k++) {
Cart.checkQty(document.getElementsByClassName(‘c-cart__item__form__qty’)[_k], true);
}
}

if (Cart.timer !== null) {
clearTimeout(Cart.timer);
}

document.getElementsByClassName(‘js-cart-count’)[0].innerHTML = _temp.getElementsByClassName(‘js-cart-count’)[0].innerHTML;
if (Cart.currentTotal === parseInt(_temp.getElementsByClassName(‘js-cart-count’)[0].innerHTML)) {
alert(‘Sorry, we could not add any more of this product to your cart as stock is limited.’)
} else {
Cart.currentTotal = parseInt(_temp.getElementsByClassName(‘js-cart-count’)[0].innerHTML);
}

document.documentElement.classList.add(‘state-cart-open’);

Resize.doResize();
}
}
_xhr.open(“POST”, this.getAttribute(‘action’), true);
_xhr.setRequestHeader(‘Content-Type’, ‘application/json’);
_xhr.send(_json);
});
}

},

  • Helper.docReady(Boot.site) → Boot.site() → Cart.init() → Cart.initAdd()

  • Cart.initAdd() binds the first .scope-product-form form (single-form binding) and posts JSON to the form action. On response it swaps .c-cart__wrapper/.c-cart, updates .js-cart-count, opens the drawer, and calls Resize.

409 Conflict Response details:
Request URL: https://kloke.com.au/cart/update.js?from_update_cart_attributes
Request Method: Post
X-Request-ID: a0a37a04-00e3-4cee-90b9-a549aef86442-1760677210
Request Payload: (sanitized):
{
“attributes”: { “GE_frtToken”: “[redacted]” }
}

I have a HAR file for you, would you mind sharing an email address or a secure upload link where I can send it privately? Please let me know if you need anything else

Thanks,
Dow

Hey @Dow_Dodds - thanks for sharing all of that, really appreciated! Happy to take that HAR file as well. I can set up a DM with you in the forums here if that works and if you’re open to sharing the link that way (via Google Drive, etc.), that would be more secure. I’ll speak with you there in just a second :slight_smile: