Skip to content

Custom Tabs postMessage channel breaks after full-page navigation on Chrome 150 (worked ≤ 149) #602

Description

@getinkranthi

Custom Tabs postMessage channel breaks after full-page navigation on Chrome 150 (worked on ≤ 149)

Environment

androidx.browser <fill in, e.g. 1.8.0>
Works on Chrome for Android 137, and 149.0.7827.201
Broken on Chrome for Android 150.0.7871.x (<fill in exact build>)
Repro Consistent across devices; confirmed by downgrading Chrome on the same device (150 → 149/137 restores it)

Summary

We use the Custom Tabs / TWA postMessage bridge (per the official guide and the twa-post-message demo) for two-way native↔web communication.

It worked reliably through Chrome 149. Starting with Chrome 150, the postMessage channel is no longer usable after a full-page navigation / reload in the web content (e.g. logout, which does window.location → full document reload). This breaks all native↔web messaging until the app is restarted.

What works (Chrome ≤ 149, including 149.0.7827.201)

  1. warmup()newSession()requestPostMessageChannel(origin, origin, extras).
  2. onRelationshipValidationResult returns result = true.
  3. onMessageChannelReady fires.
  4. session.postMessage(...) returns RESULT_SUCCESS (0).
  5. Inbound web→native messages arrive in onPostMessage.
  6. After a full-page reload (logout), on NAVIGATION_FINISHED we re-request the channel, it re-entangles, and messaging resumes.

What breaks on Chrome 150

After a full-page reload (fresh document load in the same Custom Tab):

  1. onNavigationEvent(NAVIGATION_FINISHED) fires as before.
  2. requestPostMessageChannel(...) returns true.
  3. onRelationshipValidationResult returns result = true (origin validates).
  4. onMessageChannelReady fires.
  5. But session.postMessage(...) returns -3 (RESULT_FAILURE_MESSAGING_ERROR), and no inbound web→native message is ever delivered (onPostMessage is never called for the new document).
  6. The channel never re-entangles for the reloaded document; it stays dead until the app is restarted.

So on Chrome 150 the API reports success at every step (channel requested → validated → "ready"), yet the actual message port is not usable for the freshly-loaded document.

Digital Asset Links are valid (misconfiguration ruled out)

use_as_origin verifies as linked for our origin via Google's checker:

GET https://digitalassetlinks.googleapis.com/v1/assetlinks:check
    ?source.web.site=https://<our-origin>
    &relation=delegate_permission/common.use_as_origin
    &target.android_app.package_name=<our.package>
    &target.android_app.certificate.sha256_fingerprint=<our:fingerprint>

→ { "linked": true, "maxAge": "..." }
  • PostMessageService is declared with android:exported="true".
  • Our integration matches the official twa-post-message demo.

So this is not a manifest / asset-links / integration misconfiguration.

Additional observation on Chrome 150

Calling requestPostMessageChannel(...) appears to re-trigger onRelationshipValidationResult repeatedly, which did not happen on ≤ 149. If an app re-requests the channel from within onRelationshipValidationResult, this now creates a validate → request → validate loop that did not exist before — suggesting the validation/entanglement timing around requestPostMessageChannel changed in Chrome 150.

Expected behavior

After a full-page navigation, re-requesting the channel on NAVIGATION_FINISHED (with onMessageChannelReady firing and validation succeeding) should produce a usable channel — postMessage should return RESULT_SUCCESS and inbound messages should be delivered to onPostMessage, as on Chrome ≤ 149.

Actual behavior

onMessageChannelReady fires, but postMessage returns -3 and no inbound messages are delivered; the channel is never usable for the reloaded document.

Steps to reproduce

  1. Integrate postMessage per the official guide (bare scheme://host origin, use_as_origin in assetlinks.json, PostMessageService exported).
  2. Establish the channel and confirm two-way messaging works.
  3. In the web content, trigger a full-page reload (e.g. window.location.href = ..., as logout does).
  4. On NAVIGATION_FINISHED, re-request the channel.
  5. Observe: requestPostMessageChanneltrue, validation → true, onMessageChannelReady fires, but postMessage-3 and onPostMessage is never called.
  6. Downgrade Chrome to 149 / 137 → the same code works again.

Question

Was Custom Tabs postMessage / use_as_origin entanglement changed in Chrome 150 (possibly as part of the June 2026 Custom Tabs origin-validation security hardening)?

If so, what is the correct way for an app to re-establish a usable postMessage channel after a full-page navigation on Chrome 150+?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions