Android App Links autoVerify Failed? 12 Causes and Fixes
autoVerify failed and links open the browser or a chooser? Check assetlinks.json with curl, then pm get-app-links on the device. 12 causes, each with a fix.
TL;DR: Android App Links autoVerify fails for 12 reasons, and the top production cause is an
assetlinks.jsonthat lists the upload key instead of the Play app signing key. Confirm the file withcurl -sI https://yourdomain.com/.well-known/assetlinks.json(a direct200, no redirect). Then runadb shell pm get-app-links com.example.myappand expectverifiednext to each host.
No terminal handy? Paste your domain into the free assetlinks.json validator.
The 12 reasons fall into four layers: server (1 to 3), SHA-256 fingerprints (4 to 6), package and manifest (7 to 11), and device state (12).
Find the Failing Layer First
App Link verification has four layers: the server, the file contents, the manifest, and the device. Run the checks in this table from top to bottom. The first row whose result does not match is where to start.
| Symptom | Check | Expected result | Reasons |
|---|---|---|---|
| Verification never completes, or every host fails | curl -sI https://yourdomain.com/.well-known/assetlinks.json | 200, content-type: application/json, no Location header | 1 to 3 |
| Works in debug, fails from Play | Play Console, App integrity, Play app signing | The file lists the app signing key SHA-256, in uppercase | 4 to 6 |
| One flavor fails | adb shell pm list packages | grep yourapp | The installed ID equals package_name | 7 |
| A chooser dialog appears | Open AndroidManifest.xml | android:autoVerify="true" on an https filter with the exact host | 8 to 11 |
| The file and manifest look right | adb shell pm get-app-links com.example.myapp | verified next to each host | 12 |
Each reason follows one pattern: the symptom, the check, and the fix. App Links fail quietly. There is no crash and no error toast. The link opens a browser or an "Open with" chooser, and you are left guessing. A developer writing on Medium in late 2025 captured it: "Okay, here's where I wasted like 2 days of my life... the annoying SHA-256 certificate issue that had me pulling my hair out for days." For the iOS equivalent, see the Universal Links Open Safari? Fix Your apple-app-site-association File and Universal Links Not Working? 15 Causes and the Fix for Each.
1. A Redirect Sits in Front of the File
Symptom: Verification fails for every host, and the file looks fine in a browser.
Check:
curl -sIL https://yourdomain.com/.well-known/assetlinks.json
Look for any 301 or 302. Android's troubleshooting guide says the system will not verify any App Links for your app if a redirect exists such as http://example.com to https://example.com, or example.com to www.example.com.
Fix: Remove the redirect so the file resolves with a direct 200 on the exact host in your manifest. This is the most common server-side cause.
2. The File Is Not Served as Valid JSON at the Right Path
Symptom: The URL answers 200 and the device still records a failure.
Check: The file must live at exactly https://yourdomain.com/.well-known/assetlinks.json. Unlike the iOS file, it has a .json extension. Confirm the content type and the body:
curl -sI https://yourdomain.com/.well-known/assetlinks.json
curl -s https://yourdomain.com/.well-known/assetlinks.json | jq .
Android's docs say the file is served as application/json, so do not serve text/plain or text/html. If jq errors, the file is not valid JSON: a trailing comma, a stray comment, or a byte-order mark breaks parsing. The format is a JSON array of statement objects:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
]
}
}
]
Fix: Move the file to /.well-known/, correct the content type, and repair the JSON. To check the same things without a terminal, paste your domain into our free assetlinks.json validator.
3. The Verifier Cannot Reach the File
Symptom: The file works from your laptop and not for the verifier.
Check: Look for four blockers. A login wall, basic auth, or a bot-protection challenge blocks the verifier, so the .well-known path has to be public. The HTTPS certificate must be valid and trusted: a self-signed or expired certificate fails, and there is no "proceed anyway." Android's troubleshooting guide also says example.com. (with a trailing period) must serve the same content as example.com. The device itself needs network access during verification.
Fix: Open the path to the public, renew or replace the certificate, and make the trailing-period host answer like the normal one.
4. The File Lists the Upload Key Instead of the Play App Signing Key
Symptom: App Links verify on a build you signed locally and fail on the build installed from Google Play.
Check: With Play App Signing, two keys exist. The upload key proves an upload is from you. Google's app signing key re-signs the app before distribution, and that is the certificate on the user's device. Android's docs say that the value from running keytool locally will usually not match the one on users' devices. Open Play Console, go to App integrity > Play app signing, and compare the App signing key certificate SHA-256 with your file.
Fix: Add the App signing key certificate SHA-256 to sha256_cert_fingerprints. Do not copy the Upload key certificate value. The same page shows a ready-made Digital Asset Links snippet.
5. A Signing Key You Actually Install Is Missing From the List
Symptom: Store builds verify and your internal APK or debug build does not, or the reverse.
Check: Read the SHA-256 of the build that fails.
# Debug build
keytool -list -v -keystore ~/.android/debug.keystore \
-alias androiddebugkey -storepass android -keypass android
# Locally signed release
keytool -list -v -keystore release.jks -alias your-alias
# Straight from a built APK (the most reliable)
apksigner verify --print-certs app-release.apk
Fix: The array is a list on purpose, and Android documents it as the way to support different versions of your app, such as debug and production builds. Add every certificate that signs a build you want verified:
"sha256_cert_fingerprints": [
"AA:BB:...:Play app signing key",
"CC:DD:...:release key, only if you install builds signed with it",
"EE:FF:...:debug key, if you want debug builds verified"
]
Google's Statement List Generator outputs the exact JSON from a domain, package name, and SHA-256. Our assetlinks.json generator builds the same file.
6. The SHA-256 Has the Wrong Format
Symptom: The right key is in the file and verification still fails.
Check: Android's troubleshooting guide says the value must be uppercase, and lists a lowercase value as a common mistake, alongside signing with a debug certificate while only the release value is in the file. The format is colon-separated hex pairs, such as 14:6D:E9:.... A single wrong character fails verification with no useful error.
Fix: Copy the value from apksigner or Play Console rather than retyping it, keep it uppercase, and keep the colons.
7. The package_name Does Not Match the Installed applicationId
Symptom: One flavor or build type verifies and another does not.
Check: The package_name must exactly match the applicationId installed on the device, not the manifest package attribute if you remap it and not a flavor suffix you forgot.
// build.gradle.kts
android {
defaultConfig { applicationId = "com.example.myapp" }
productFlavors {
create("pro") { applicationIdSuffix = ".pro" } // -> com.example.myapp.pro
}
}
If the device runs the pro flavor, the verifier looks for com.example.myapp.pro. Confirm the installed ID with adb shell pm list packages | grep example.
Fix: Add a statement object for each applicationId you ship. Add a second object rather than cramming two packages into one.
8. The Intent Filter Has No autoVerify
Symptom: The link works but shows a chooser dialog instead of opening the app directly, and pm get-app-links shows none after you waited and re-ran verification.
Check: Android's troubleshooting guide says each <intent-filter> must include android:autoVerify="true". Without it, Android never fetches your assetlinks.json. No autoVerify, no verification.
Fix:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="yourdomain.com" />
<data android:pathPrefix="/product" />
</intent-filter>
9. The Filter Has the Wrong Shape
Symptom: autoVerify is set and the host never appears in the verification output.
Check: Verification only runs for a filter that uses the http or https data scheme (not a custom scheme) and includes the VIEW action plus the BROWSABLE and DEFAULT categories. If any is missing, the system does not treat the filter as an App Link.
Fix: Match the filter in reason 8 exactly, and keep the scheme as https.
10. The Host in the Manifest Is Not the Host You Link To
Symptom: Links to yourdomain.com verify and links to go.yourdomain.com do not.
Check: android:host="yourdomain.com" does not cover www.yourdomain.com or links.yourdomain.com. The host in the filter, the host in your links, and the host serving assetlinks.json must be the same string. Android's docs say that if your app links support multiple host domains, you must publish assetlinks.json on each domain.
Fix: Declare every host you link to, and serve a correct file at each one's own /.well-known/assetlinks.json. A wildcard host like *.yourdomain.com needs a reachable file at each subdomain you actually use.
11. One Broken Host Fails the Rest
Symptom: Hosts you configured perfectly fail too.
Check: On Android 11 and lower, the app becomes the default handler only if the Digital Asset Links files match all the hosts in the manifest. One typo or one host without a file can fail all of them. Android's guide also says test-only non-verifiable links can fail verification for all App Links on Android 11 and lower. Android 12 and later query the file for each unique host name. Curl every host:
for h in yourdomain.com go.yourdomain.com share.yourdomain.com; do
echo "== $h =="
curl -sI "https://$h/.well-known/assetlinks.json" | head -n 1
done
Fix: Fix or remove the broken host, and keep only hosts you can serve the file from. A failed host falls back to the chooser dialog on every version.
12. The Device Is Holding Stale or Unfinished State
Symptom: The server, file, and manifest all pass and the device still reports a failure.
Check: Ask the device what it recorded. Wait at least 20 seconds after an install, because verification is asynchronous.
adb shell pm get-app-links com.example.myapp
Add --user cur to read the state for the current user. Read the state next to each domain:
| State | Meaning | Next step |
|---|---|---|
verified | The domain passed and the app handles its links | Done |
none | Nothing recorded yet; verification may still be running | Wait a few minutes, re-verify, check again; if it stays none, check autoVerify |
1024 or higher | A device-specific verifier code, not one cause | Confirm network access, re-verify, check the file (reasons 1 to 3) and the SHA-256 (4 to 6) |
legacy_failure | The legacy verifier rejected the domain for an unknown reason | Reset and re-verify |
approved / denied | Force-set through the shell, not earned by verification | Fine for testing |
migrated / restored | Preserved from a legacy result or a data restore | Re-verify to confirm |
system_configured | Approved by the device configuration | Not an error |
Fix: On Android 12 and later, clear stale state and verify again (the reset command comes from Android's verification guide):
# Reset the recorded state for all of this package's domains
adb shell pm set-app-links --package com.example.myapp 0 all
# Trigger a fresh verification pass
adb shell pm verify-app-links --re-verify com.example.myapp
# Confirm
adb shell pm get-app-links com.example.myapp
Android's documentation says to wait a few minutes for a manual verification. Check Settings > Apps > your app > Open by default > Add link too: a verified link shows as supported. On Android 15 and later, the system also re-verifies in the background, and a change to your file can take up to seven days to reach devices that do not re-verify manually. On Android 14 and lower, the system picks up a change only at install or update. The reliable reset is the three commands above, or an uninstall and reinstall.
To compare with what Google sees, query the Digital Asset Links API, the endpoint the verifier uses:
curl -s "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://yourdomain.com&relation=delegate_permission/common.handle_all_urls" | jq .
A healthy response has a statements array, and each entry carries target.androidApp.packageName and target.androidApp.certificate.sha256Fingerprint. An empty response sends you back to reasons 1 to 3. A SHA-256 that differs from your device's is reason 4 in black and white. On Android 17 and later, adb shell am start --debug-link -a android.intent.action.VIEW -d "https://yourdomain.com/product/123" shows which apps and intent filters match a URL.
The Short Version
App Link opens a chooser / browser, not the app
│
▼
curl -sI the assetlinks.json URL ──► not 200 / redirect / wrong type / blocked? ──► reasons 1 to 3
│ ok
▼
SHA-256 = Play APP SIGNING key, uppercase, every signing key listed? ──► no ──► reasons 4 to 6
│ yes
▼
package_name matches the installed applicationId? ──► no ──► reason 7
│ yes
▼
intent-filter has autoVerify + https + exact host, every host served? ──► no ──► reasons 8 to 11
│ yes
▼
adb pm get-app-links shows "verified"? ──► no ──► reset + re-verify, then wait (reason 12)
How WarpLink Handles the Setup
Reasons 1 to 3 are about hosting a byte-perfect assetlinks.json. When you register an app in WarpLink, we generate the file from the package name and SHA-256 fingerprints you provide and serve it with Content-Type: application/json, no redirects, and no auth wall. Each app has its own link host, {handle}.aplnk.to, with its own assetlinks and AASA pair, because iOS and Android match a link host to one app. Verified custom domains get their own pair, and the files are regenerated when you update the app.
To check the result, open https://{host}/test on your app's host. It is a setup page served at the edge, so you can confirm the host is live before you run pm get-app-links. On your side, set the autoVerify filter with the exact host and register the Play App Signing SHA-256 from Play Console (reason 4). The SDK side is one call: pass the incoming intent URI to WarpLink.handleDeepLink(uri) from onCreate() and onNewIntent(), and it returns the destination, custom parameters, and attribution data on the main thread, for cold and warm start. The full setup is in the Android deep links docs, and the deep linking page shows what an app registration generates.
Frequently Asked Questions
Why do my Android App Links work in debug but not in production?
Almost always the SHA-256 certificate fingerprint. Your debug build is signed with your debug or upload key, but Play App Signing re-signs the distributed app with Google's signing key, which has a different SHA-256. Your assetlinks.json lists the wrong one, so autoVerify passes locally and fails in the Play Store build. Copy the App signing key SHA-256 from Play Console under App integrity > Play app signing and add it to the file.
Where do I get the correct SHA-256 for assetlinks.json?
For a Play-distributed app, copy it from Play Console: App integrity > Play app signing, under "App signing key certificate" (not "Upload key certificate"). For a locally signed release, read it from your release keystore with keytool -list -v -keystore release.jks -alias your-alias, or from a built APK with apksigner verify --print-certs app-release.apk. List every signing key you use in the sha256_cert_fingerprints array.
What does state 1024 or legacy_failure mean in pm get-app-links?
1024 (or higher) is a device-specific verifier code, and legacy_failure is a rejection with an unknown reason. Neither names one cause. Confirm network access, re-run adb shell pm verify-app-links --re-verify <package>, then check the file and any redirect (reasons 1 to 3) and the SHA-256 (reasons 4 to 6). Reset with adb shell pm set-app-links --package <pkg> 0 all, re-verify, and wait a few minutes before you read the state.
Why does autoVerify show a chooser dialog instead of opening my app?
Either the intent filter is missing android:autoVerify="true" (so Android never verifies and defaults to the chooser), or verification ran and failed. Run adb shell pm get-app-links <package>: none means no result is recorded yet, so wait, re-run adb shell pm verify-app-links --re-verify <package>, and check again. 1024 or higher, or legacy_failure, has no single cause: confirm network access, then check the file and the SHA-256. Check the manifest and the JSON file as separate steps.
How do I force Android to re-verify App Links without reinstalling?
Run adb shell pm verify-app-links --re-verify <package>, then check the result with adb shell pm get-app-links <package>. On Android 12 and later, reset stale state first with adb shell pm set-app-links --package <package> 0 all. Wait a few minutes before you read the result, and note that re-verification needs network access. Reinstalling still works as a fallback, since Android re-verifies at install time.
Does the SHA-256 in assetlinks.json need to be uppercase?
Yes. Android's troubleshooting guide says the value should be uppercase, written as colon-separated hex pairs such as 14:6D:E9:.... A lowercase value is a common reason a correct-looking file still fails verification.
Sources
- Android Developers, Verify Android App Links: the
autoVerifyfilter,pm get-app-links,pm verify-app-links --re-verify,pm set-app-links --package <package> 0 all, the verification states, the 20 second and few-minute waits, the Android 11 and Android 12 host behavior, and the Android 15 re-verification delay. - Android Developers, Troubleshoot App Links: no redirects, uppercase values, the trailing-period host, the Play App Signing certificate,
autoVerifyon each filter, andam start --debug-linkon Android 17 and later. - Android Developers, Declare website associations: file location,
application/json, multiple certificates, multiple apps and the Play Console signing snippet. - Google, Digital Asset Links getting started and the statements.list reference: the statement format and the API that reason 12 queries.
Related Guides
- Start here: What Is Deep Linking? How Mobile Deep Links Work on iOS and Android covers how App Links, Universal Links, and deferred deep links fit together, including the App Links section.
- The iOS sister checklists: Universal Links Not Working? 15 Causes and the Fix for Each and Universal Links Open Safari? Fix Your apple-app-site-association File.
- Test it properly: How to Test Deep Links: simctl and adb Commands, Plus a Pre-Launch Checklist covers
adb shell pm get-app-links,pm verify-app-links, and the rest of the verification commands in one place. - After the link opens: 10 Deep Linking Mistakes That Break Install Attribution.
- Migrating off Firebase Dynamic Links: the Firebase Dynamic Links Migration Guide includes the assetlinks.json and intent-filter setup as part of the cutover, and Firebase Dynamic Links alternatives compared weighs your replacement options.
- Reference: the Android deep links docs, the deep linking concepts page, and the pricing page.
Once your App Links verify and open the right screen, the next question is attribution: which link, campaign, or channel drove the open and the install, and how those opens show up in your analytics. That is where WarpLink picks up after the link resolves.
Check the file with the free assetlinks.json validator, then let WarpLink host and update this file so a key or package change is one edit.
Get your first deep link working today
Deep linking, install attribution, and real-time analytics in one SDK. Free for 10K clicks a month, no sales call.
WarpLink is link infrastructure that sends one link to the right place on iOS, Android, and the web, with install attribution and real-time analytics on every tap.
Stuck on setup? Email support@warplink.app and we will help you get one real link working.
WarpLink Team
Building affordable, reliable link infrastructure for mobile teams. Deep linking, install attribution, and real-time analytics in one SDK.
Related Posts
Deep Links in Instagram and Facebook In-App Browsers: The Fixes
A deep link tapped in the Instagram, Facebook, or TikTok in-app browser usually will not open your app. What each one does on iOS and Android, and the fixes.
Email Deep Linking: Why Tracked Links Open the Browser, Not Your App
Email click tracking rewrites links to a tracking domain, so Universal Links never fire. See the mechanism, three fixes, and provider support.
How to Test Deep Links: simctl and adb Commands, Plus a Pre-Launch Checklist
Test deep links on iOS and Android with xcrun simctl and adb, verify Universal Link and App Link state, then run a pre-launch checklist and Maestro CI recipe.