"The deep link is not working" is one of the vaguest bug reports a mobile team receives. The link could fail at any point: DNS resolution, verification file lookup, OS interception, app launch, or screen routing. This guide gives you a systematic approach to identify exactly where the failure occurs.
For testing tools, see best deep link testing tools and how to use them. For common challenges, see common deep linking challenges and how to solve them.
The Deep Link Resolution Flow
A deep link goes through these stages. Failure at any stage breaks the experience:
1. User taps link
2. OS checks if the domain has a verified app association
3. If verified: OS opens the app and passes the URL
4. App receives the URL and routes to the correct screen
5. Screen renders with the correct content
If step 2 fails: browser opens instead (fallback)
If step 4 fails: app opens but shows wrong screen or home screen
If step 5 fails: screen loads but content is wrong or missing
Step 1: Check the Verification Files
iOS (AASA)
The Apple App Site Association file must be:
- At
https://yourdomain.com/.well-known/apple-app-site-association - Served with
content-type: application/json - Returned with HTTP 200 (no redirects)
- Valid JSON with the correct
applinksstructure
# Check status and content-type
curl -sI https://yourdomain.com/.well-known/apple-app-site-association
# Expected:
# HTTP/2 200
# content-type: application/json
# Check content
curl -s https://yourdomain.com/.well-known/apple-app-site-association | python3 -m json.tool
Common AASA errors:
| Error | Symptom | Fix |
|---|---|---|
| 404 Not Found | All Universal Links fail | Deploy the AASA file to the correct path |
| 301/302 Redirect | Links open in browser | Remove redirect, serve directly |
text/html content-type |
Links open in browser | Configure server to return application/json |
Wrong Team ID in appID |
Links open in browser | Fix: appID must be TEAMID.bundleID |
Missing route in paths / components |
Specific routes fail | Add the route pattern to the AASA |
Android (assetlinks.json)
# Check the file
curl -s https://yourdomain.com/.well-known/assetlinks.json | python3 -m json.tool
# Verify via Google's API
curl -s "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://yourdomain.com&relation=delegate_permission/common.handle_all_urls"
Common assetlinks.json errors:
| Error | Symptom | Fix |
|---|---|---|
| Wrong package name | App Links not verified | Match exactly: com.your.app |
| Wrong SHA-256 fingerprint | App Links not verified | Use the signing certificate fingerprint, not the upload key |
| Multiple signing certs (Play App Signing) | Verification fails | Include both the upload key and the Play signing key fingerprints |
File not at /.well-known/ |
All App Links fail | Move file to correct location |
Step 2: Check the OS-Level State
iOS
On iOS 14+, check Universal Links diagnostics:
Settings > Developer > Universal Links > Diagnostics
- Enter your domain.
- Check if the AASA was downloaded successfully.
- If it shows "Error," the AASA file has an issue.
Console.app (macOS)
- Connect the iOS device to a Mac.
- Open Console.app, filter by
swcd(the process that manages Universal Links). - Look for error messages about AASA download or validation.
Resetting Universal Links cache
- Reinstall the app (triggers AASA re-download).
- Or wait up to 24 hours for iOS to refresh the AASA cache.
Android
# Check App Links verification status adb shell pm get-app-links com.your.package # Expected output: # com.your.package: # ID: ... # Signatures: [...] # Domains: yourdomain.com # Status: verified ← This must say "verified" # If status is "none" or "ask", verification failed # Re-verify: adb shell pm verify-app-links --re-verify com.your.packageStep 3: Check the Link Source
Where the user clicks the link affects whether it works:
Source iOS Universal Links Android App Links Notes Safari Works N/A Must be a tap on a link, not typed in address bar Chrome (iOS) Works Works Notes app Works N/A Good for testing iMessage Works N/A Reliable test method Gmail app Blocked (in-app browser) Works iOS: opens in Gmail's WebView Instagram Blocked Partially works In-app browser on both platforms Facebook Blocked Partially works In-app browser on both platforms If the link works from Notes/iMessage but not from Gmail/Instagram, the issue is the in-app browser, not your deep link configuration.
Step 4: Check App-Side Routing
If the app opens but shows the wrong screen (or the home screen):
iOS (SwiftUI)
// Check if onOpenURL is handling the link .onOpenURL { url in print("Received URL: \(url)") print("Path: \(url.path)") print("Query: \(url.query ?? "none")") // Check if your router handles this path let handled = router.handle(url) print("Handled: \(handled)") }iOS (UIKit)
// Check if the app delegate or scene delegate is receiving the URL func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard let url = userActivity.webpageURL else { print("No URL in user activity") return false } print("Received Universal Link: \(url)") return handleDeepLink(url) }Android
// Check if the Activity receives the Intent override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val action = intent.action val data = intent.data Log.d("DeepLink", "Action: $action") Log.d("DeepLink", "Data: $data") Log.d("DeepLink", "Path: ${data?.path}") Log.d("DeepLink", "Query: ${data?.query}") if (data != null) { val handled = deepLinkRouter.handle(data) Log.d("DeepLink", "Handled: $handled") } }Common app-side issues:
Issue Symptom Fix Router does not handle the path Home screen shown Add the route to your router Route parameter extraction fails Wrong content shown Check regex/pattern matching Screen requires authentication Login screen shown instead of content Queue the deep link, handle after login Race condition with app initialization Sometimes works, sometimes does not Ensure deep link handler runs after initialization Step 5: Check Deferred Deep Links
If the issue is specifically with deferred deep links (user installs the app and expects to land on a specific screen):
Debugging the Match
// Log the matching process on first app open async function handleFirstOpen(deviceInfo: DeviceInfo) { console.log('First open device info:', deviceInfo); const match = await matchDeferredDeepLink({ ip: deviceInfo.ip, userAgent: deviceInfo.userAgent, timestamp: deviceInfo.firstOpenTimestamp }); console.log('Match result:', { found: match !== null, confidence: match?.confidence, originalUrl: match?.originalUrl, clickTimestamp: match?.clickTimestamp, timeDelta: match ? Date.now() - new Date(match.clickTimestamp).getTime() : null }); if (match) { navigateTo(match.originalUrl); } }Common deferred deep link failures:
Issue Cause Fix No match found Too much time between click and install Extend matching window No match found IP changed (WiFi to cellular) Add fingerprint attributes beyond IP Wrong match Multiple users on same network Add more distinguishing attributes Match found but wrong screen Route parsing issue Fix the route handler Debugging Decision Tree
Deep link not working? │ ├─ Does the AASA/assetlinks.json return 200 with correct content-type? │ ├─ No → Fix the file hosting │ └─ Yes ↓ │ ├─ Does the OS show the domain as "verified"? │ ├─ No → Check Team ID, bundle ID, package name, SHA-256 fingerprint │ └─ Yes ↓ │ ├─ Does the app open when tapping the link in Notes/Chrome? │ ├─ No → Check app installation, entitlements, Intent filters │ └─ Yes ↓ │ ├─ Does the app open from the original source (email, social, etc.)? │ ├─ No → In-app browser issue. Use a bounce page strategy. │ └─ Yes ↓ │ ├─ Does the correct screen show? │ ├─ No → Debug the app-side route handler │ └─ Yes → Deep link is working. Check analytics data instead.Tolinku for Debugging
Tolinku provides real-time analytics showing open rates, fallback rates, and error rates per route, making it easy to identify which deep links are failing and why. See the troubleshooting documentation for iOS-specific and Android-specific debugging guides.
For testing tools, see best deep link testing tools and how to use them. For the complete guide, see the complete guide to deep linking in 2026.
Get deep linking tips in your inbox
One email per week. No spam.