POS 11.6.1 Breaking Changes: Native Overlay Dismissal (hideCameraScanner / window.close) and Full-Screen Camera Scanner

Since POS update 11.6.1, we have identified three breaking changes affecting POS UI Extensions that use the scanner and modal APIs:

  1. shopify.scanner.hideCameraScanner() no longer works. Calling the method executes without error (confirmed via logs), but the camera scanner overlay remains on screen and does not dismiss. The only way to close it is for the user to manually press the X button. This is confirmed via setTimeout testing, the function fires correctly but has no effect on the native camera overlay.
  2. window.close() no longer reliably closes the extension modal when called from an async context. Calling window.close() from within a chain of awaited Shopify API calls (e.g. after cart.addCartCodeDiscount) does not dismiss the modal in production. The same call works correctly when triggered directly from a user interaction (button press). This may share a root cause with the hideCameraScanner() issue, both are commands sent from the extension to dismiss a native overlay, and both stopped working in 11.6.1. It appears POS 11.6.1 may have changed or broken how the native layer handles dismissal signals originating from async extension code.
  3. The camera scanner now renders full-screen within the modal. Prior to 11.6.1, the camera scanner rendered as a partial overlay within the modal view. It now takes up the full modal screen, which is a breaking UI change for extensions that display UI alongside the scanner.

All three issues are reproducible on production devices running POS 11.6.1 and were not present on earlier versions.

@Gunes Thanks for reporting, we’ll take a look immediately. We’ll keep you posted

Hey JS, I think this might be related as well [Bug] Urgent POS Extension Camera detection - #2 by Gunes

Hey @JS_Goupil thanks for the quick rollback mentioned here, working as expected on my device now! :victory_hand:

No problem @Gunes , we will roll it back out but it should not affect 2026-01. 2026-04 and onward should have the full screen experience though

Hey @JS_Goupil

I upgraded the API version from 2026-01 to 2026-04, but I’m still experiencing the same issue: the camera overlay remains open even after calling shopify.scanner.hideCameraScanner().

Has the rollback already been deployed, and if so, is it expected to resolve this issue?

Thank you!

Same here @JS_Goupil , I can’t close the camera scranner.

@JS_Goupil Hi! Checking back in here - I am trying to upgrade our app the the latest version and this bug would be a blocker in us being able to do so. Are there any updates on the status of fixing the inability to close the camer scanner? Or should we be using an older version?

Hey all, just catching up. @Adif The rollback was for a separate issue where the new UX for the scanner (full screen) was leaked to 2026-01. We fixed that. As of 2026-04, full screen is the default.

That said, it should definitely be closing when calling hideCameraScanner(). Was this a regression with a POS version? I’ll take a look at this today

Edit: It appears to be a regression in POS 11.9.x, I’m going to see if we can get a fix in ASAP

Thanks so much! Please let me know if you need any additional information from our side - yes, it was specific to hideCameraScanner() but all other components appeared to be working as normal!

Any updates on this issue? Do you know when a fix will be released for the hideCameraScanner?

@Tiago_Ramalho We have a partial fix going out shortly. There’s still a weird edge case where if the merchant taps the x button before you call dismiss, it could be a double dismissal, but we’ll get to that next week. This is at least mostly functional.

Hey JS,
I think there’s another bug here, where if the user closes the camera by clicking the X, and then call hideCameraScanner it crashes the POS extension

Hi @JS_Goupil, thanks for the partial fix,hideCameraScanner() does dismiss the camera again on our devices. However, we’re now seeing a new issue with it, and I want to flag it because it doesn’t match the edge case you described (merchant taps X before we call dismiss).

Since the partial fix, calling hideCameraScanner() programmatically dismisses both the camera scanner AND the extension modal, with no X tap involved.

Repro (pos.home.modal.render, api_version 2026-04, POS 11.9.1):

  1. Extension modal opens a screen that shows the camera and subscribes to scans:

useEffect(() => {
shopify.scanner.showCameraScanner();
return () => shopify.scanner.hideCameraScanner();
}, []);

const unsubscribe = shopify.scanner.scannerData.current.subscribe((result) => {
if (!result.data) return;
shopify.scanner.hideCameraScanner(); // hide camera, stay in the modal
lookupProduct(result.data); // renders a form in the same modal
});

2. Scan any barcode.
3. Expected: camera overlay closes, extension modal stays open showing our form.
4. Actual: camera closes and the extension modal is dismissed with it, the user is dropped back to the smart grid. It looks like the single programmatic dismissal is applied to the whole native stack (camera overlay + parent modal).

Could you confirm whether this week’s full fix covers this programmatic path as well, and not only the X-tap-before-dismiss ordering?

@Tiago_Ramalho yes, the root issue is that there’s nothing safeguarding a double dismissal. The fix we look into early this week should cover that.

Although in your case it shouldn’t be resulting to that without the users tap on X. Any chance your teardown is being triggered?

Thanks! We dug a bit deeper and noticed something we don’t fully understand: it looks
like multiple instances of our extension are running at the same time, after one
single camera scan, our backend receives three identical requests. Each instance also
calls hideCameraScanner(), so the hide effectively fires multiple times, and that
seems to be what closes the whole modal (no X tap involved).

Is it expected that multiple instances run and receive scans at the same time? And
would the fix you mentioned also cover this case?

@Tiago_Ramalho As of API version 2025-10, each screen in your navigator is running a separate instance. So if you’re subscribed globally to the scanner in each instance (not conditionally, like in a component that you know will be mounted), it will be listening in each instance. Kind of similar to how native UIViewControllers work in iOS - they’re still alive underneath, you need to make sure to clean them up. The best practice is to only subscribe if your component gets mounted, but alternatively you can listen to navigation.currentEntry to sub/unsub as well.

Alright, I understand. That said, we migrated the extension to API version 2026-04 a long time ago and this wasn’t happening before, so it does seem to be a side effect of your recent changes/fixes. Either way, we’ll try to work on our end to fix this behaviour.

Appreciate your quick reply!

@Tiago_Ramalho Interesting, I’ll double check.

I just ran into the window.close() not firing after running await’d code in my extension. Was working fine for months and then after publishing a new version of my extension today this started happening. I even tried reverting my changes but the issue is still present.

I’m seeing this in API version 2026-01 and POS versions 11.9.1 & 11.10.0