Until now, mutations resolved as successful even when the cart didn't actually change. Starting with 2026-10, the promise rejects when POS can't confirm the write, so every call site needs error handling.
Mutations resolve whether or not POS applied the write. Apps can't tell applied writes from dropped ones, so they may show a fee or discount as "applied" even though the cart hasn't changed.
Mutations resolve only when POS confirms the write. Otherwise they reject, so the app can respond right away by retrying, showing staff a message, or rolling back its own state.
Note: These categories were added on this page for readability and aren't in the original article.
These mutations are new in 2026-10 and have rejected unconfirmed writes since their first release. No version with the old behavior exists.
POS didn't accept the write, and the cart is unchanged. This happens when POS is no longer accepting cart changes, for example after the cashier has moved on to payment. Once the cart is editable again,a retry may succeed.
The outcome is unknown, andthe write may have been partially applied.Re-reading the cart before retryingis required.
| Case | Cart state | Recommended action |
|---|---|---|
| refused | Unchanged | Retry once the cart is editable again |
| could not confirm | Unknown (possibly partially written) | Re-read the cart, then retry |
| Other rejections | Not specified | Handle each cause separately, such as an invalid discount value or a customer that can't be found |
api_version = "2026-10" or later: apps whose POS UI extensions call even one of the mutations above.
Current behavior continues unchanged. 2026-07 remains usable until its API versioning support period ends.
Instead of continuing as if an unconfirmed write had succeeded, your existing catch / .catch() handler will now run.
await shopify.cart.applyCartDiscount('FixedAmount', 'Winter discount', '5.00');
setDiscountApplied(true);
try {
await shopify.cart.applyCartDiscount('FixedAmount', 'Winter discount', '5.00');
setDiscountApplied(true);
} catch (error) {
setDiscountApplied(false);
showToast('Could not update the cart. Try again before payment.');
}
Check every place in your extension that calls the affected cart mutations.
Add a try/catch or .catch() , and write code that rolls back state and shows a message.
api_version = "2026-10" Upgrade toOnly bump the version once error handling is in place.
If a rejection isn't caught, neither post-write UI rendering nor loading-state cleanup will run. "The spinner never goes away" and "the confirmation step gets skipped" are typical symptoms. Put cleanup somewhere that also runs on errors.
With refused, the cart hasn't changed, so you can retry as-is once it's editable again. With could not confirm, the write may have partially gone through, sore-read the cart firstbefore retrying. Retrying blindly risks applying the change twice.
Calls can also reject for reasons other than these two messages, such as an invalid discount value or a customer that can't be found. As a rule, catch every rejection, and only check the message when you need to tell the two cases apart.
The scope is set not by the store but by each extension's api_version . Apps with multiple extensions can migrate gradually, adding error handling to one extension at a time before upgrading it.
Added in 2026-10, updateLineItemQuantity is designed to reject from its very first release. If you switch to this mutation, design on the assumption that a resolved call means the cart changed, and write a catch from the start.
api_version = "2026-10" .setDiscountApplied(false)). Don't forget to clear the loading state too.api_version to 2026-10. Leave the others on 2026-07 for now.