We’re hitting the same thing from a different app, also iOS only, also working fine on Android. A few findings that may help narrow it down, since some of them contradict the explanations we’ve been given.
**The same code works from every other surface.** Our extension declares targets on the order details screen, the Smart Grid and post-purchase, and they render the same components through the same hooks against the same endpoints — the entrypoint files differ by a single line, a diagnostic label. Creating and voiding a record completes normally from the order details screen and from the Smart Grid modal, on the same device, in the same session. Only post-purchase fails. Two measurements make the contrast concrete:
- In the Smart Grid modal, a response that took over ten seconds resolved without any trouble.
- We ran the same `setInterval` probe on both surfaces. On the order details block it fired on schedule, with drift in the single and double digits of milliseconds. On the post-purchase action target it never fired once.
**Request duration isn’t the variable.** Some of our failing requests complete in well under a second and the Promise still never settles. In one instrumented print attempt, our own fetch returned in under a second, POS went on to download the PDF, and then `api.print.print()` never resolved and the native print dialog never appeared.
**Timers don’t fire either, so a UI-level timeout can’t rescue it.** We instrumented a repeating timer plus deadlines at 5 s and 20 s. With the target still mounted, and after waiting a long time, none of them ever ran — no timer callback, no deadline, no repaint. So keeping a UI-level timeout around the flow isn’t implementable on this target.
**Leaving the screen doesn’t help.** This is the part we haven’t seen tested elsewhere. We tried navigating out of the post-purchase modal with the `shopify:point-of-sale/orders/` deep link. Navigation works and we land on the order details screen — the surface that normally works — and registering there still hangs. We also tried `window.close()`: the modal closes and the hang persists.
So it doesn’t appear to be the modal, and it doesn’t appear to be the screen. It looks like the post-purchase flow itself, still active in the POS session, starving the shared JS runtime regardless of where the user navigates.
**A workaround, in case it helps anyone else hitting this:** if the cashier ends the post-purchase flow natively first (New sale) and only then opens the extension from another surface, everything completes normally. We ended up moving the operation off the post-purchase target entirely, and that has been reliable on iOS.
—
**Details requested above:**
- **Shopify POS app version:** 11.11.0
- **iOS version and device model:** the instrumented session ran on an iPhone13,4. Worth noting that the user agent we receive reports two different system versions, and we’re passing both on verbatim without interpreting them: `iPhone OS 18_7` in the WebKit segment, and `iOS/26.5.2` in the POS segment. Full string:
`Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Shopify POS/11.11.0/iOS/26.5.2/Apple/iPhone13,4/production ExtensibilityHost`
The deep link and `window.close()` tests described above were run on an iPad.
- **POS UI Extensions API version:** `api_version = “2026-07”`, with `@shopify/ui-extensions` pinned exactly to `2026.7.1`.
- **Does this reproduce against a known public HTTPS endpoint?** Our backend is a public HTTPS endpoint with a valid certificate — not a tunnel — and the requests arrive and are answered normally. We haven’t tried a third-party endpoint yet, and can do so if it’s useful. Worth pointing out that the print case involves no request of ours at all: `api.print.print()` is your own API, and it hangs the same way, which is hard to explain as a problem with our endpoint.
Given all of the above — two independent apps, sub-second requests, and a mounted target where nothing asynchronous ever completes — is this still considered no currently known issue? The statement that `fetch()` should resolve or reject while the extension target is mounted doesn’t hold in our measurements.
Happy to share the instrumented traces with anyone from the POS extensions team who wants them.