[Bug] fetch() hangs on iOS in pos.purchase.post.action.render

We have a POS UI Extension registered on pos.order-details.action.render and pos.purchase.post.action.render. Both targets use identical fetch() logic hitting the same backend endpoints. In fact, they use the exact same modal, so literally the same logic.

On iOS only, fetch Promises on the post-purchase target hang indefinitely; they never resolve or reject.

Our server is not the problem. Multiple clients hit the same API with sub-second response times. Server logs confirm every request from the iOS extension arrives and gets a response. The order-details target, in the same app on the same device, works perfectly. The hang only occurs on pos.purchase.post.action.render.

iOS only. iPadOS and Android both work fine on both targets. Same code, same backend; only iOS post-purchase hangs.

The response never reaches JS. The request leaves the device and hits our server (confirmed via logs). Our server responds. But the fetch() Promise stays pending forever. The request path works; something in the response path between NSURLSession and the JS Promise is dropping it.

AbortController doesn’t help. Calling controller.abort() on a hung fetch does not cause the Promise to reject with AbortError; it just stays pending. The abort signal isn’t propagating to the native layer.

Given that the order-details target works perfectly with the same code, this looks like something specific to the post-purchase target’s JS runtime or WebView lifecycle on iOS. We don’t have native-side visibility to narrow it further.

Seems similar to this, which is marked as resolved

  1. Is there a known issue with fetch responses not surfacing to JS Promises in the post-purchase target on iOS?
  2. Is there a runtime/WebView lifecycle difference between pos.order-details.action.render and pos.purchase.post.action.render on iOS?

Hi, thanks for the detailed report. To answer your questions,

  1. No currently known issue with fetch from pos.purchase.post.action.render.
  2. Yes, there are lifecycle differences between pos.order-details.action.render and pos.purchase.post.action.render because they’re entered from different
    parts of POS. That said, fetch() should still resolve or reject on both targets while the extension target is mounted.
    i.e.- If the user navigates away, dismisses the modal, or the post-purchase screen tears down while a request is still in flight, the extension instance may be terminated and the pending Promise should not be expected to complete.

The key thing to confirm is whether the Promise hangs while the post-purchase modal remains open and mounted. Can you help share some details which helps narrow it down:

  • Shopify POS app version
  • iOS version and device model
  • POS UI Extensions API version
  • App ID.
  • Approximate timestamp when it happened?
  • Are the iOS/Android devices in the same location/network set up?
  • Whether this reproduces against a known public HTTPS endpoint?

In the meantime, I’d recommend keeping a UI-level timeout around this flow so merchants aren’t left on an indefinite loading state.
Thanks!

If you connect the debugger you can step through the request and response code. That might help incidate the source of the issue Development and debugging