Summary
TWA packaged with horizonOSAppMode: 'immersive' launches WebXRCustomTabActivity in the VR compositor, but the web content never renders. The Horizon OS interstitial (pulsating dots) persists indefinitely, followed by "App name unavailable" error. The same TWA works correctly with horizonOSAppMode: '2D'.
Environment
| Field |
Value |
| Headset |
Meta Quest 2 |
| Firmware |
Android 14 (SDK 34), Build UP1A.231005.007.A1 |
| Quest Browser |
144.2.0.29.52.912191064 |
| Bubblewrap |
Meta fork v1.22.3 (latest main) |
| androidbrowserhelper |
2.5.0 |
| horizonplatformsdk |
1.1.0 |
| Host OS |
Linux (Ubuntu, x86_64) |
| JDK |
OpenJDK 17.0.18 |
Configuration
twa-manifest.json (relevant fields):
{
"packageId": "in.walkinto.xr",
"host": "walkinto.in",
"startUrl": "/viewer/xr/vr",
"horizonOSAppMode": "immersive",
"isMetaQuest": true,
"minSdkVersion": 23
}
AndroidManifest.xml includes:
<category android:name="com.oculus.intent.category.VR" />
<meta-data android:name="horizonos.pwa.APP_MODE" android:value="immersive" />
<meta-data android:name="com.meta.horizon.platform.ovr.OCULUS_APP_ID" android:value="\ 26873319878939517" />
<uses-permission android:name="android.permission.INTERNET" />
- Digital Asset Links verified (
assetlinks.json live, fingerprints match)
Reproduction Steps
bubblewrap build with horizonOSAppMode: 'immersive'
adb install app-release-signed.apk (or upload to RC channel via ovr-platform-util)
- Launch app from Quest Library → Unknown Sources (sideload) or from Store (RC channel)
Observed Behaviour
LauncherActivity starts → Horizon OS recognises it as immersive:
VolumetricContentMonitor: Switching to immersive app. taskId: 3
CustomTabsService binding fails (expected on Quest):
W ActivityManager: Unable to start service Intent { act=android.support.customtabs.action.CustomTabsService pkg=com.oculus.browser } U=0: not found
- After ~12s delay,
WebXRCustomTabActivity starts:
I ActivityTaskManager: START u0 {act=android.intent.action.VIEW dat=https://walkinto.in/viewer/xr/vr pkg=com.oculus.browser cmp=com.oculus.browser/.WebXRCustomTabActivity}
- Activity gains focus then immediately loses it and stops:
cr_XRActivity: onResume! ... windowHasFocus? false
cr_XRActivity: onWindowFocusChanged! ... true
cr_XRActivity: onWindowFocusChanged! ... false ← instant loss
cr_XRActivity: onPause! ...
cr_XRActivity: onStop! ...
- SurfaceSyncGroup times out after 2 seconds:
E SurfaceSyncGroup: Failed to receive transaction ready in 2000ms. Marking SurfaceSyncGroup(wmsSync-VRI[WebXRCustomTabActivity]#26) as ready
E SurfaceSyncGroup: Failed to receive transaction ready in 2000ms. Marking SurfaceSyncGroup(VRI[WebXRCustomTabActivity]#27) as ready
E SurfaceSyncGroup: Failed to receive transaction ready in 2000ms. Marking SurfaceSyncGroup(SurfaceView[com.oculus.browser/com.oculus.browser.WebXRCustomTabActivity]#25) as ready
- User sees Horizon loading dots indefinitely, then "App name unavailable" toast.
Key Finding: Page IS Loaded
Despite the blank screen, Chrome DevTools (chrome://inspect) shows the page is fully loaded and rendered:
$ curl -s http://localhost:9222/json
[{ "title": "WalkInto XR", "url": "https://walkinto.in/viewer/xr/vr", "type": "page" }]
Evaluating JS on the page returns full DOM with all content (lobby UI, tours, navigation). The page renders correctly — the issue is that the VR compositor surface never connects to display it.
2D Mode Works
Switching to horizonOSAppMode: '2D' with com.oculus.intent.category.2D — the exact same URL loads and renders correctly as a flat panel in Horizon OS. This confirms the web content, Digital Asset Links, INTERNET permission, and TWA binding all work. Only the immersive surface pipeline fails.
Tested Both Sideload and Store (RC Channel)
- Sideloaded via
adb install: same SurfaceSyncGroup timeout
- Uploaded to RC channel via
ovr-platform-util upload-quest-build: same SurfaceSyncGroup timeout
- Real
OCULUS_APP_ID set in manifest: same result
Questions
- Is immersive TWA mode (
horizonOSAppMode: 'immersive') currently functional on Quest Browser v144?
- Are there additional prerequisites beyond what bubblewrap generates (e.g., Horizon Store approval, specific browser flags, developer mode settings)?
- Is the
WebXRCustomTabActivity surface pipeline expected to work for sideloaded/RC-channel apps, or only for production store-distributed apps?
- The
ovr-platform-util create-pwa command requires a pwa-template.apk — is there a current version of this template available? The one in the pwabuilder-oculus repo (June 2022) is incompatible with current apktool versions.
Workaround
Using horizonOSAppMode: '2D' as a working baseline. Users can still enter WebXR immersive sessions from within the 2D panel via navigator.xr.requestSession('immersive-vr').
Summary
TWA packaged with
horizonOSAppMode: 'immersive'launchesWebXRCustomTabActivityin the VR compositor, but the web content never renders. The Horizon OS interstitial (pulsating dots) persists indefinitely, followed by "App name unavailable" error. The same TWA works correctly withhorizonOSAppMode: '2D'.Environment
main)Configuration
twa-manifest.json (relevant fields):
{ "packageId": "in.walkinto.xr", "host": "walkinto.in", "startUrl": "/viewer/xr/vr", "horizonOSAppMode": "immersive", "isMetaQuest": true, "minSdkVersion": 23 }AndroidManifest.xml includes:
<category android:name="com.oculus.intent.category.VR" /><meta-data android:name="horizonos.pwa.APP_MODE" android:value="immersive" /><meta-data android:name="com.meta.horizon.platform.ovr.OCULUS_APP_ID" android:value="\ 26873319878939517" /><uses-permission android:name="android.permission.INTERNET" />assetlinks.jsonlive, fingerprints match)Reproduction Steps
bubblewrap buildwithhorizonOSAppMode: 'immersive'adb install app-release-signed.apk(or upload to RC channel viaovr-platform-util)Observed Behaviour
LauncherActivitystarts → Horizon OS recognises it as immersive:CustomTabsServicebinding fails (expected on Quest):WebXRCustomTabActivitystarts:Key Finding: Page IS Loaded
Despite the blank screen, Chrome DevTools (
chrome://inspect) shows the page is fully loaded and rendered:Evaluating JS on the page returns full DOM with all content (lobby UI, tours, navigation). The page renders correctly — the issue is that the VR compositor surface never connects to display it.
2D Mode Works
Switching to
horizonOSAppMode: '2D'withcom.oculus.intent.category.2D— the exact same URL loads and renders correctly as a flat panel in Horizon OS. This confirms the web content, Digital Asset Links, INTERNET permission, and TWA binding all work. Only the immersive surface pipeline fails.Tested Both Sideload and Store (RC Channel)
adb install: same SurfaceSyncGroup timeoutovr-platform-util upload-quest-build: same SurfaceSyncGroup timeoutOCULUS_APP_IDset in manifest: same resultQuestions
horizonOSAppMode: 'immersive') currently functional on Quest Browser v144?WebXRCustomTabActivitysurface pipeline expected to work for sideloaded/RC-channel apps, or only for production store-distributed apps?ovr-platform-util create-pwacommand requires apwa-template.apk— is there a current version of this template available? The one in the pwabuilder-oculus repo (June 2022) is incompatible with current apktool versions.Workaround
Using
horizonOSAppMode: '2D'as a working baseline. Users can still enter WebXR immersive sessions from within the 2D panel vianavigator.xr.requestSession('immersive-vr').