When using Shopify Functions (fulfillment constraints) to restrict delivery options, there is no deterministic way to detect when a checkout becomes blocked (no shipping options available) using the Checkout UI Extensions API.
Current behavior
When a fulfillment constraint function excludes all locations for a given shipping address:
-
Customer enters shipping address
-
Shopify evaluates fulfillment constraints
-
All locations are excluded (no delivery options available)
-
Shopify displays native blocked banner: “Shipping isn’t available for this delivery address”
-
BUT the Delivery API provides no signal that calculation is complete and blocked
Expected behavior
The Delivery API should provide a deterministic signal to indicate:
-
When delivery calculation is in progress vs complete
-
When checkout is blocked due to no available delivery options
-
Clear error/blocked state that extensions can react to
Technical details
API version
2025-10
What we observe
Blocked checkout state:
deliveryGroups: [] // Stays empty forever
buyerJourney: {
"completed": false,
"steps": [{"handle": "checkout", ...}],
"activeStep": {"handle": "checkout"}
}
checkoutSettings: {
"orderSubmission": "ORDER",
"shippingAddress": {"isEditable": true}
}
Normal checkout (loading state - same initially):
deliveryGroups: [] // Also empty while calculating
buyerJourney: {
"completed": false,
"steps": [{"handle": "checkout", ...}],
"activeStep": {"handle": "checkout"}
}
checkoutSettings: {
"orderSubmission": "ORDER",
"shippingAddress": {"isEditable": true}
}
After 1-10 seconds (variable):
deliveryGroups: [{
deliveryOptions: [...],
selectedDeliveryOption: {...}
}]
The problem
There is no way to distinguish between:
-
Delivery options are still calculating (normal)
-
Delivery options will never appear (blocked)
Both states show:
-
deliveryGroups: [] -
Identical
buyerJourney -
Identical
checkoutSettings -
No
isLoading,isCalculating, or error state
Current workaround (unreliable)
The only workaround is timer-based detection:
// Wait X seconds, if deliveryGroups still empty → assume blocked
setTimeout(() => {
if (deliveryGroups.length === 0 && hasValidAddress) {
// Probably blocked? Maybe just slow?
}
}, 3000); // How long to wait? 3s? 5s? 10s for B2B?
Why this fails:
-
Large B2B checkouts can take 10+ seconds to calculate
-
Complex carts with multiple line items take longer
-
Slow networks delay calculation
-
No perfect timeout value exists
-
False positives (normal checkouts logged as blocked)
-
False negatives (blocked checkouts not detected if timeout too long)
Proposed solutions
Option 1: Add isCalculating flag
deliveryGroups: {
value: [],
isCalculating: true // NEW
}
Option 2: Add error/blocked state
deliveryGroups: {
value: [],
error: {
type: "NO_DELIVERY_OPTIONS_AVAILABLE",
message: "Shipping isn't available for this delivery address"
}
}
Option 3: Expand buyerJourney with validation states
buyerJourney: {
validationErrors: [
{
step: "checkout",
field: "shippingAddress",
error: "NO_DELIVERY_OPTIONS"
}
]
}
Use case
We want to:
-
Detect when a checkout is blocked by fulfillment constraints
-
Log these events to help merchants understand why checkouts fail
-
Alert merchants when products/addresses are being blocked
-
Improve merchant fulfillment configuration
Currently impossible without unreliable timer-based heuristics.
Reproduction steps
-
Create a Fulfillment Constraint function that excludes all locations for specific addresses
-
Create a Checkout UI Extension that monitors
deliveryGroups -
Enter a shipping address that triggers the constraint
-
Observe: No API signal indicates calculation is done and blocked
-
Only option: Use arbitrary timeout and guess
Environment
-
API version: 2025-10
-
Extension type: Checkout UI Extension (
ui_extension) -
Framework: Preact
-
Extension target:
purchase.checkout.shipping-option-list.render-after
Additional context
Shopify displays a native blocked banner, which means Shopify knows the checkout is blocked internally. This information should be exposed to extensions via the Delivery API.