$0 / 100% discounted exchange items do not create a fulfillmentOrder after returnProcess

Summary

When calling returnProcess on a return that contains a fully discounted ($0.00) exchange item, no FulfillmentOrder is created. This means the exchange item gets stuck in an “Unreleased exchange items” state indefinitely and cannot be fulfilled programmatically.

Steps to reproduce

  1. Create two returns with an exchange, one priced at $10 and one at $0 (100% discount applied)
  2. Call returnProcess on the return
  3. Query fulfillment orders on the order

For the $10 exchange item:

  • :white_check_mark: A FulfillmentOrder is created as documented
  • :white_check_mark: We can call fulfillmentOrderReleaseHold to release it and proceed to fulfillment

For the $0 exchange item:

  • :cross_mark: No FulfillmentOrder is created after returnProcess
  • :cross_mark: There is nothing to call fulfillmentOrderReleaseHold on
  • :cross_mark: The item shows as “Unreleased exchange items” in the Shopify Admin UI indefinitely

Additional observation

If we manually click “Process return” in the Shopify Admin UI for the $0 exchange, it does release the fulfillment correctly. So the UI path works but the API path does not produce the same result.

Questions

  1. Is this intentional behaviour? The docs state that returnProcess creates FulfillmentOrder objects for exchange items, is a $0 item a known exception?
  2. Is there a workaround to programmatically release/fulfill a $0 exchange item via the API when no FulfillmentOrder is created?

This is causing real customer impact as orders with $0 exchange items are stuck and not being fulfilled automatically. Any guidance would be appreciated.

1 Like

Hey @Rohan-Frate thanks for the clear write up, it made this easy for me to look into!

The returnProcess docs state that the mutation “creates FulfillmentOrder objects for exchange items” without any exception for zero-value items. The migration guide also draws a clear distinction between FulfillmentOrder creation and hold placement. Even or refundable exchanges should still get FulfillmentOrders created, they just won’t be placed on hold. So the expected behavior for your $0 exchange is that a FulfillmentOrder is created (with no hold), not that it’s skipped entirely.

I tested this on a dev store by creating two returns with exchanges via the API, one at full price and one with a 100% discount ($0). Both produced a FulfillmentOrder after calling returnProcess, which is the expected behavior. So I wasn’t able to reproduce the issue on my end, but that doesn’t rule out something specific to your store or flow.

To dig into why it’s behaving differently for you, could you reproduce the $0 exchange case and capture the x-request-id from the response headers for each step? Ideally I’d want the x-request-id from the returnCreate call, the returnProcess call, and the fulfillmentOrders query where you see no FulfillmentOrder.

With those I can trace the full sequence internally and get this in front of the right people if need be. Thanks!

Hey @Donal-Shopify!

Thanks for looking into this.

Sure thing, here are the request ID’s from the same flow recreated on my local development shop.

  1. returnCreate:

"x-request-id": "47a2923b-fcef-4fd5-bcb8-dbbf6ea11f1a-1776270151"

  1. returnProcess:

"x-request-id": "46a3dd58-1b43-4491-a0d6-20f459e9886e-1776270180"

  1. order.fulfillmentOrders query which returns no fulfillmentOrders for the unprocessed exchange item:

"x-request-id": "4391db3b-3cec-431a-9154-7aa84eae9387-1776270181"

Let me know what you find!

Hey @Donal-Shopify ,

To clarify, another nuance in this scenario is that the returned item is also a 100% discounted to begin with. So, there are no transactions. The transaction modal on the order page looks something like this:

Thanks for the request IDs, @Rohan-Frate. I traced through all three and can confirm what you’re seeing.

The returnCreate call correctly set up the exchange with the 100% discount and created reverse fulfillment orders as expected. The issue is in the returnProcess step. For your $0 exchange, the step that creates FulfillmentOrders for exchange items is not being triggered. The return is then auto-closed and the exchange item ends up stuck with no FulfillmentOrder to act on.

Your clarification about the original item also being 100% discounted (zero transactions on the order) was the key detail. That’s what distinguishes your scenario from mine when I tested earlier, and it appears to be why the FulfillmentOrder creation step gets skipped.

I’ve raised this with the relevant team internally along with the trace data from your request IDs. There’s no API workaround I can offer right now since the FulfillmentOrder simply isn’t being created, and you can’t fulfill what doesn’t exist. I’ll follow up here once I hear back from the team.

Thanks @Donal-Shopify ! Please do keep me updated as this is actively affecting merchants.

1 Like