No API signal to detect blocked checkout from fulfillment constraints

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:

  1. Customer enters shipping address

  2. Shopify evaluates fulfillment constraints

  3. All locations are excluded (no delivery options available)

  4. Shopify displays native blocked banner: “Shipping isn’t available for this delivery address”

  5. 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:

  1. Delivery options are still calculating (normal)

  2. 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:

  1. Detect when a checkout is blocked by fulfillment constraints

  2. Log these events to help merchants understand why checkouts fail

  3. Alert merchants when products/addresses are being blocked

  4. Improve merchant fulfillment configuration

Currently impossible without unreliable timer-based heuristics.

Reproduction steps

  1. Create a Fulfillment Constraint function that excludes all locations for specific addresses

  2. Create a Checkout UI Extension that monitors deliveryGroups

  3. Enter a shipping address that triggers the constraint

  4. Observe: No API signal indicates calculation is done and blocked

  5. 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.