“Unfortunately, there are no carriers available for your delivery address”: why it happens on an old recovered cart

by Luca Cavallini Support cases
“Unfortunately, there are no carriers available for your delivery address”: why it happens on an old recovered cart

The symptom

A customer opens an old cart, either from a recovery link sent by a back-office operator or from a cart-recovery email, and gets all the way to the "Shipping method" step without any issue. There they read: "Unfortunately, there are no carriers available for your delivery address." No other error, no warning before that point. If the same customer starts a brand new cart with the exact same address, checkout works fine and carriers show up immediately. The cart involved is usually not recent, it can be months or years old, and it has just been reopened through a recovery link.

What it looked like, and wasn't

The natural suspicion, faced with a message that explicitly mentions the "delivery address," is that the address itself is the problem: deleted, no longer linked to the customer, or simply not covered by any configured shipping zone. None of these three leads holds up under checking.

  • The cart's address (at header level) is valid. It is not deleted, it is the customer's current address, and it maps to a geographic zone that has active carriers.
  • Carriers exist and are active for that zone. Checked manually from the back office (Shipping -> Carriers): several options are configured and active for that zone, including a paid carrier and a free pickup option.
  • The customer's group has access to those carriers. No group restriction excludes the account involved.

Three reasonable leads, all confirmed correct, and the error still showed up. At that point the right question was not "which carriers exist for this address," but "is PrestaShop actually computing carriers for this address, or for a different one."

The real cause

For every cart, PrestaShop keeps two separate references to a delivery address: one at the cart's header level (ps_cart.id_address_delivery), and one for each individual product line (ps_cart_product.id_address_delivery). This dual structure exists to support multi-shipping, meaning a single cart with different parcels shipped to different addresses. On a store that does not use multi-shipping, both values should always match.

The catch is that PrestaShop never updates an existing address when a customer edits it from their account: it creates a new one and soft-deletes the old one (deleted = 1), keeping it in history so past orders placed against it are not broken. On an active cart this goes unnoticed: the next time the customer walks through checkout, the cart header gets updated to the new address. But if that cart is abandoned before checkout completes, the saved product line stays linked to the old address ID, the one the customer later deletes by editing it. The header can later get updated on its own (for example by a follow-up visit that only touches the billing address), leaving a silent mismatch between header and product line that neither side reports by itself.

The method that computes shipping options, Cart::getDeliveryOptionList(), groups parcels by the address stored at product-line level, not the header, that is exactly how it correctly supports multi-shipping when it is actually used. With a deleted line address, it still computes a list of carriers (for the deleted ID, which still exists as a record, just flagged inactive), but checkout then compares that list against the header address, which is different. No match, no carrier shown, even though carriers for the real address exist and are perfectly valid.

Why this mostly hits old carts: PrestaShop never deletes an abandoned cart, it stays in the database indefinitely, and a recovery link reopens that exact same record, with its exact same history. The longer the gap between when a cart was created and when it gets recovered, the more likely it is that the address saved on that line has been edited at least once in the meantime.

The fix

Two separate levels of intervention are needed: the carts that are already broken right now, and the ones that will break the same way in the future.

One-off fix, for carts already out of sync: a query that realigns every product line with a deleted address back to the header address, but only when the header address is valid and different from the line's.

UPDATE ps_cart_product cp
 JOIN ps_cart c ON c.id_cart = cp.id_cart
 JOIN ps_address ap ON ap.id_address = cp.id_address_delivery
 JOIN ps_address ah ON ah.id_address = c.id_address_delivery
 SET cp.id_address_delivery = c.id_address_delivery
 WHERE cp.id_address_delivery <> 0
   AND ap.deleted = 1 AND ah.deleted = 0
   AND cp.id_address_delivery <> c.id_address_delivery;

On a cart table with years of history, this query can touch several hundred rows in one pass: in the case that surfaced this issue it was 565 rows, across 221 different carts.

Structural fix, for future carts: an override of Cart::getDeliveryOptionList() that runs the same realignment, but only for the single cart being opened, right before computing shipping options. In practice, every old cart "heals itself" the moment it reaches checkout, with no need to periodically sweep the whole database.

One detail worth stating explicitly: this realignment assumes that a mismatch between line and header address is always an error to fix, which is only true if the store does not use multi-shipping. If multi-shipping were ever turned on, different addresses per line become legitimate, and this fix would need to be disabled or rethought, it would break exactly the case it is supposed to support.

One last category was deliberately left untouched: carts where both the line address and the header address are deleted. There is no valid address left to realign to, so this query cannot fix them. These are old carts that were never reopened again; if they ever are, the normal checkout flow will ask the customer to pick or confirm a valid address before proceeding anyway, so they resolve themselves the moment they're actually used.

How to verify it holds

  • Reopen a cart with the known mismatch (or reproduce one: a deleted line address, a header on a valid address) and check that the shipping step shows the expected carriers, not the error message.
  • After running the one-off fix, recount how many rows still have a line address that is deleted and different from the header: it should come back to zero.
  • Verify in code that the override is actually the one loaded (get_class() on the Cart instance during checkout, or a method_exists check), not just that the file exists on the server: an override can be present and inactive if the class cache has not been regenerated.
  • Repeat the check periodically if the store recovers a lot of old carts through email or remarketing campaigns: that is exactly where this problem resurfaces most, because it is exactly the kind of cart that accumulates this mismatch over time.

The store where this case was diagnosed stays anonymous on purpose. If a recovered PrestaShop cart shows "no carriers available" while a new cart with the same address works fine, the right place to look is not the carrier configuration, it is whether the address saved on the product line and the one saved on the cart header are actually the same record. For ongoing maintenance on a PrestaShop store, including this kind of case, there is a summary of services on this page.

Leave a comment

Comments are moderated before publication.

← Blog

Loading...
Back to top