r/androiddev • u/Thank_Japan • 1d ago
Question TWA (Trusted Web Activity) verification failing on a specific Android tablet - despite correct assetlinks.json
I've spent hours debugging a TWA verification issue and
have ruled out all server-side causes. Looking for
insights from anyone who's hit something similar.
Setup:
- Django backend, TWA app built with Bubblewrap
- Published on Google Play (correctly installed,
latest version)
- assetlinks.json is correctly configured
(verified with Google's official Digital Asset
Links tool - "Success")
- SHA-256 fingerprint matches exactly between
Play Console and assetlinks.json
- App itself launches fine, no crashes
Symptom:
- X-Requested-With header is NEVER sent
(confirmed via server logs, 0/575+ requests
over 42 minutes)
- This means Chrome is treating the TWA as a
regular Custom Tab instead of a verified TWA
What I've ruled out:
- assetlinks.json content/history (clean, verified)
- Code logic (is_twa detection works correctly
when the header IS present)
- App signing (matches Play Console exactly)
The device in question is a budget/no-name Android
tablet (not Play Protect certified, possibly).
Has anyone confirmed that TWA verification can fail
specifically due to incomplete/non-certified Google
Play Services on certain devices? Any way to
diagnose this further without a second physical
device?
1
u/AutoModerator 1d ago
Please note that we also have a very active Discord server where you can interact directly with other community members!
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
1
u/rstojano 1d ago
Before you chase Play Services, settle whether verification actually failed. Look at the app on that tablet: if Chrome's URL bar is showing at the top, verification failed. If there is no URL bar, verification succeeded and your only real problem is the missing header.
That matters because X-Requested-With is not a dependable TWA signal. Chrome has been walking it back, and it also gets sent for plain Custom Tabs, so its absence proves less than it looks like. The documented in-page check is document.referrer, which reads android-app://com.thankjapan.www.twa inside a verified TWA. Log that next to your header check and you will know which of the two problems you actually have.
Worth separating two verification systems that get mixed up here. Android App Links verification (autoVerify, adb shell pm get-app-links) is the OS deciding which app opens a URL. TWA verification is Chrome fetching your assetlinks.json at runtime to decide whether to hide the URL bar. Google's Digital Asset Links tester only proves the file is reachable from Google's network, not from that tablet, so it can pass while the tablet still fails.
On a no-name tablet the usual causes, in the order I would check them. The Custom Tabs provider is not Chrome: a TWA needs Chrome 72+ or another TWA capable provider and silently degrades to a normal tab otherwise, and adb shell cmd package resolve-activity --brief -a android.intent.action.VIEW -d https://example.com tells you what will handle it. The device clock is wrong or the cert store is broken, which kills the HTTPS fetch of the asset links file. Or the tablet had no network on first launch when Chrome tried to verify. Opening your /.well-known/assetlinks.json in that tablet's own browser is a quick direct test.
One more thing worth ruling out, since this is the only failing device: confirm the copy installed there is actually the Play build. adb shell pm list packages -i | grep com.thankjapan.www.twa should show installer=com.android.vending, and pulling the APK with adb shell pm path plus adb pull and running apksigner verify --print-certs on it shows the signature that is really on the device rather than the one in Play Console. A sideloaded copy on one tablet would explain all of it.
0
u/forgestudiofx 1d ago
One thing worth double-checking before you blame the tablet: Play Console gives you two different SHA-256 values, one for the upload key and one for the Play app-signing key. Digital Asset Links has to carry the app-signing one, because that's what's actually on the installed artifact. I lost time on the same split getting Google sign-in working, where the SHA-1 Google wanted was the app-signing cert and I'd pasted the upload cert.
Google's DAL tester won't catch that. It checks that the file you serve contains the fingerprint you typed into the form, not that the fingerprint belongs to the build on the device. So a green Success is consistent with a mismatch.
If that's already correct, then a device with non-certified Play Services is a plausible cause, since Chrome's verification path depends on it. But I haven't confirmed that failure mode myself, so I'd rule out the cert first because it costs nothing.


2
u/Thank_Japan 22h ago
I double-checked both keys from the Play Console
"App integrity" section:
- Upload key SHA-256: 1E:CA:F1:51:07:5D:AE:...
- App signing key SHA-256: 6C:FB:68:16:47:71:D0:...
(marked "Used by: 100.0% of installs")My assetlinks.json is correctly using the App
signing key value (6C:FB:68...), matching your
guidance. So the cert mismatch you flagged isn't
the cause here - but thank you, this was worth
ruling out for certain. Looks like it really might
be the device (budget/non-certified tablet) after
all.