Your Android App Links are broken. A tap opens Chrome instead of your app, or a new user installs from your campaign and lands on the home screen. Android shows no error. The link simply goes somewhere else.
The good news: one adb command prints Android's verification verdict for every host your app claims. Most teams never run it, and debug the wrong layer for days.
This guide is the Android companion to our iOS deep link failure guide, which covers Universal Links. Same format: six failure modes, each with symptom, cause, diagnosis and fix.
How does Android decide whether a link opens your app?
Android App Links are HTTPS links that Android opens directly in your app, without a chooser, once the system has verified through a Digital Asset Links file on your domain that the app and the website belong together. They work on Android 6 and later on devices with Google services.
The resolution chain:
- Your manifest declares an intent filter for your domain with
android:autoVerify="true". - On install or update, Android fetches
https://yourdomain/.well-known/assetlinks.jsonfor each host. - If the file names your package and signing certificate, that host is marked verified.
- When a user taps a link to a verified host, Android opens your app. If not verified, the link opens in the browser.
Tech Explainer: the Android 12 change. Since Android 12, a web link opens your app only if your app is approved for that exact domain. Otherwise it goes straight to the default browser, with no chooser, whatever your app targets. Before Android 12, an unverified link usually still showed a chooser that included your app, so many broken setups went unnoticed.
A deep link URI scheme such as myapp://product/42 skips verification entirely. It opens the app when installed and fails when it is not.
Failure mode 1: assetlinks.json cannot be read
Symptom: links open Chrome on every device, including fresh installs.
Cause: Android could not fetch or parse the file. Google's requirements are strict:
- Served over HTTPS, even if your intent filter also declares
http. - No 301 or 302 redirects. An http to https or non-www to www redirect on the domain stops verification of all your App Links.
- Content-type
application/json, and valid JSON. - Reachable by Google's verifier: a WAF or bot challenge blocks it like a 404.
Diagnosis:
curl -I https://yourdomain.com/.well-known/assetlinks.json
Expect 200 and application/json, with no Location header. Then ask Google directly through the Digital Asset Links API:
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://yourdomain.com&relation=delegate_permission/common.handle_all_urls
An empty result means Google cannot see your statement. The Statement List Generator and Tester does the same in a browser.
Fix: serve the file at the exact path on every host your manifest claims, remove redirects for that path, and exempt it from bot protection. If you use a branded link domain, the same rules apply to it; our custom link domain guide covers DNS and SSL.
Failure mode 2: the SHA-256 fingerprint does not match
Symptom: the file loads, but links still open Chrome on Play Store installs, while debug builds may work.
Cause: the sha256_cert_fingerprints value does not match the certificate that signed the APK on the user's device.
- Play App Signing. If Google signs your releases, the fingerprint you get from
keytoolon your upload key usually will not match what users install. This is the most common Android-only cause. - Debug vs release. Listing only the release fingerprint breaks debug testing, and the reverse breaks production.
- Formatting. Google's troubleshooting guide asks for the uppercase, colon-separated form.
Diagnosis and fix:
- Open Play Console, Release > Setup > App signing, and copy the Digital Asset Links snippet shown there.
- The field accepts several fingerprints, so list the Play signing key, the upload key and the debug key together.
- Re-run statements:list and confirm the fingerprint matches Play Console.
Failure mode 3: the intent filter never asks for verification
Symptom: pm get-app-links (failure mode 4) lists no domains, or lists some hosts and not others.
Cause: Android only verifies intent filters that include all of:
- Action
android.intent.action.VIEW - Categories
BROWSABLEandDEFAULT - Data scheme
httporhttps, with your host android:autoVerify="true"on at least one filter
<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="links.yourdomain.com" />
</intent-filter>
Two traps sit behind this:
- Hosts you do not control. Remove old test domains and third-party hosts from intent filters. On Android 11 and lower, one host that fails verification stops all of your App Links from verifying.
- Per-host results on Android 12+. Newer versions verify each host separately. So
www.yourdomain.comcan pass whileyourdomain.comfails, and only some links break.
Fix: declare each host explicitly and serve a valid assetlinks.json on each.
Failure mode 4: verification is stale, missing or overridden
Symptom: you fixed the file, but devices still open Chrome. Or it works on your phone and fails on a colleague's.
Cause: verification is a stored result, not a live check.
- On Android 14 and lower, Android only re-reads assetlinks.json on install or update. Existing users keep the old result.
- On Android 15+, the system re-verifies in the background, but changes can take up to seven days to reach devices.
- The user can switch off "Open supported links" for your app in its "Open by default" settings, and only one app can hold a domain on a device.
Diagnosis with adb:
# What Android currently believes
adb shell pm get-app-links com.yourapp
# Reset and force a fresh check (Android 12+, device online)
adb shell pm set-app-links --package com.yourapp 0 all
adb shell pm verify-app-links --re-verify com.yourapp
# Android 17+: show which apps and filters matched a URL
adb shell am start --debug-link -a android.intent.action.VIEW -d "https://links.yourdomain.com/p/42"
Reading the state per host:
- verified: working. Look at failure mode 5 or 6.
- none: no result yet. Wait a few minutes and re-verify.
- legacy_failure or 1024 and above: rejected or errored. Check the device's network, then the file and fingerprint.
Fix: for users on Android 14 and lower, ship an app update after fixing the server. Wait at least 20 seconds after a fresh install before testing.
Failure mode 5: in-app browsers and Custom Tabs
Symptom: the link opens your app from Chrome or Gmail, but not from Instagram, Facebook or another social app.
Cause: the browser component decides whether Android is ever asked. Our in-app browser breakage guide covers both platforms; on Android:
- Raw WebViews load the URL as a page. Android never gets a chance to route it.
- Custom Tabs open installed apps by default. But if the host app has connected to the Custom Tabs service, links stay in the tab unless it opts back in with
setSendToExternalDefaultHandlerEnabled(true). - Chrome itself will not launch an app from a URL the user typed, or from a navigation without a user gesture, such as a JavaScript timer redirect.
Fix: design for the browser you cannot change.
- Send social traffic to a lightweight intermediary page with an "Open in app" button, so the hand-off is a real user tap.
- Point that button at an
intent://URL withS.browser_fallback_urlset to your store or web page, so a missing app falls back cleanly.
Linkrunner offers this pattern through its social media intermediary page.
Failure mode 6: the app is not installed
Symptom: a new user taps the campaign link, installs, opens the app and lands on the home screen.
Cause: App Links only help users who already have the app. For everyone else, Android sends the link to your website, not to the Play Store. Getting them to the right screen after install is a separate job: deferred deep linking.
How it should work on Android:
- The link service sends non-installers to the Play Store with the click context in the referrer.
- On first open, the SDK reads the Play Install Referrer. Google keeps it available for 90 days and advises reading it once on first run.
- If no referrer is available (sideloads, other app stores), a fallback match on IP and user-agent recovers some of the context.
Across Linkrunner's published benchmark, the median Android click-to-install time was around 2.4 minutes, across 200k+ matched Android pairs in April 2026. Users arrive fast, so a deferred route that loses the race to your splash screen sends them to the home screen.
Fix: initialise the SDK before your first navigation decision. Hold the launcher on the splash screen until the deferred result returns or times out. Retry the referrer read on slow devices. The Android 14 deferred deep linking post covers the timing changes in detail.
Your Android App Links debug toolkit
Run these in order. Each one rules out a layer:
- Direct test:
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://links.yourdomain.com/p/42". If the app opens, verification is fine and the problem is the environment (failure mode 5 or 6). - curl -I on assetlinks.json: status, content-type, no redirect.
- Digital Asset Links API statements:list: Google sees your package and fingerprint.
- Play Console App signing page: the right fingerprint.
- adb
pm get-app-links: the device's verdict for each host.
Two Linkrunner tools shorten steps 2 to 4: the free assetlinks.json and AASA validator, and the link-setup check in the Linkrunner MCP server, which inspects a live link and its domain from Claude or another AI client and reports blockers first.
Pre-launch checklist
Before an Android deep link campaign goes live:
- assetlinks.json returns 200,
application/json, no redirect, on every host in the manifest. - Fingerprints include the Play App Signing key from Play Console.
- Every link-domain intent filter has VIEW, DEFAULT, BROWSABLE, https and
autoVerify="true". pm get-app-linksshowsverifiedfor each host on a fresh install, on Android 12+ and on your oldest supported version.- Test from the in-app browser of every social channel in the plan, not just Chrome.
- Run a deferred test: uninstall, tap the campaign link, install from Play, confirm the right screen and the right campaign in your MMP.
Our deep link QA checklist extends this to attribution checks across both platforms.
FAQ
Why does my App Link open Chrome instead of my app?
On Android 12+, a domain your app is not verified for opens in the default browser. Run adb shell pm get-app-links with your package name; if the host is not verified, check the file, fingerprint and intent filter.
Do I need a new app build after fixing assetlinks.json? For users on Android 14 and lower, yes. Those versions only re-verify on install or update. Android 15+ re-verifies in the background, but it can take up to seven days.
Do App Links work inside Instagram or Facebook? Not reliably. These apps load links in their own browser view, so Android is never asked to route them. Use an intermediary page with an "Open in app" button.
What is the difference between an App Link and a deep link on Android? A deep link is any URL that opens a screen in your app, including custom schemes. An App Link is a verified HTTPS deep link that opens your app without a chooser and falls back to your website when the app is missing.
Fix the layer, not the symptom
Android App Links fail in layers: file, fingerprint, intent filter, stored verdict, browser and install gap. Start with pm get-app-links and work through them in order.
If you would rather not maintain intermediary pages and deferred routing yourself, Linkrunner supports App Links and Universal Links on branded link domains. Every link is deferred by default, using deferred deep linking on the Android SDK: deterministic matching first, with IP and user-agent as a fallback. Request a demo and run the checklist above against your own links.
