Troubleshoot & Test Deep Links
A comprehensive guide to diagnosing and fixing deep link issues in your app. Use this page whenever links fail to route into your app or when you need to verify your deep link configuration.
Quick Diagnosis
Before diving into detailed troubleshooting, run through this quick checklist:
- ✅ SDK Initialized: Check logs for
Bootstrap completed successfullyor similar initialization messages - ✅ API Key Valid: Ensure your project API key matches the environment (production vs development)
- ✅ Domain Configuration: URL scheme/intent filter matches the domain configured in ULink dashboard
- ✅ Domain Verified: Domain shows as "Verified" in Domains section of dashboard
- ✅ Platform Configuration: iOS (Bundle ID, Team ID) or Android (Package Name, SHA-256) configured in dashboard
- ✅ Network Access: Device has internet access (no captive portal blocking requests)
- ✅ Link Created: The link exists in your ULink dashboard and has been clicked at least once
ULink automatically serves the required verification files (AASA for iOS, Asset Links for Android) on your domain. You don't need to:
- Host these files yourself
- Deploy them to your server
- Configure your web server
The files are generated automatically based on your dashboard configuration and served at:
- iOS:
https://<your-domain>/.well-known/apple-app-site-association - Android:
https://<your-domain>/.well-known/assetlinks.json
If these files aren't accessible, check that your domain is verified and platform configuration is complete in the dashboard.
If all items are checked but links still don't work, proceed to the platform-specific sections below.
1. Basic Testing Methods
Test your deep links using these methods before diving into detailed troubleshooting.
iOS Testing
Method 1: Share via Notes or Messages (Recommended)
- Open the Notes app or Messages on your iOS device
- Paste your ULink URL (e.g.,
https://links.shared.ly/your-slug) - Tap the link
- The app should open if universal links are configured correctly
Sharing via Notes or Messages uses the same system that handles universal links in Safari, making it the most realistic test scenario.
Method 2: Simulator Command
xcrun simctl openurl booted "https://links.shared.ly/your-slug"
This command simulates tapping a link in the iOS Simulator. Check the console for link processing logs.
Method 3: Safari Browser
- Open Safari on your device
- Navigate to your ULink URL
- Tap the "Open in App" banner if it appears
- Or long-press the link and select "Open in [Your App]"
Universal links only work when:
- The link is opened from another app (not Safari directly)
- The domain is properly associated with your app
- The AASA file is accessible and valid
Android Testing
Method 1: ADB Command (Recommended)
adb shell am start \
-a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://links.shared.ly/your-slug"
This command simulates tapping a link. Your app should open and process the link.
Method 2: Share Sheet
- Share the link from any app (Chrome, Messages, etc.)
- Select your app from the share options
- The app should open and handle the link
Method 3: Chrome Browser
- Open Chrome on your device
- Navigate to your ULink URL
- If App Links are verified, the app opens automatically
- If not verified, you'll see an "Open in App" banner
Flutter Testing
For Flutter apps, use the platform-specific methods above. Additionally, check for this log message:
DEBUG: Link listeners setup complete
This confirms the Flutter SDK has initialized and is ready to receive links. If you don't see this message, the SDK may not be initialized properly.
2. Inspect Logs and Debug Output
Logs are your best friend when troubleshooting deep links. Here's what to look for:
iOS Logs
Xcode Console
When testing in Xcode, look for these log messages:
[ULink] Handling incoming URL: https://links.shared.ly/your-slug
[ULink] Processing URL: https://links.shared.ly/your-slug
[ULink] Successfully resolved ULink data: {...}
Console.app (Physical Devices)
- Connect your device to your Mac
- Open Console.app
- Select your device from the sidebar
- Filter by your bundle ID or search for "ULink"
- Tap your link and watch for processing messages
Common iOS Log Patterns
Success Pattern:
[ULink] Handling incoming URL: ...
[ULink] Processing URL: ...
[ULink] Successfully resolved ULink data: ...
Failure Pattern:
[ULink] Handling incoming URL: ...
[ULink] URL is not a ULink or resolution failed
Android Logs
Logcat
Use Android Studio's Logcat or command line:
adb logcat | grep -i ulink
Look for these messages:
ULink: Handling deep link: https://links.shared.ly/your-slug
ULink: Resolved link data:
ULink: Type: dynamic
ULink: Slug: your-slug
ULink: Parameters: {...}
Common Android Log Patterns
Success Pattern:
ULink: Handling deep link: ...
ULink: Resolved link data:
ULink: Type: dynamic
Failure Pattern:
ULink: Handling deep link: ...
ULink: Error resolving link: ...
Flutter Logs
Flutter apps show logs from both the Flutter layer and native platforms:
DEBUG: Setting up link listeners
DEBUG: Link listeners setup complete
DEBUG: Dynamic link event received: {...}
DEBUG: UI updated with dynamic link event
If you see DEBUG: Link listeners setup complete but no link events, the native platform may not be forwarding links correctly.
3. Common Issues and Solutions
Issue: Links Open in Browser Instead of App
iOS:
- Cause: Universal links not properly associated or AASA file invalid
- Solution: See "Verify iOS Universal Links" section below
Android:
- Cause: App Links not verified or intent filter misconfigured
- Solution: See "Verify Android App Links" section below
Issue: App Opens But Doesn't Navigate
Possible Causes:
- Stream listeners not set up
- Listening to wrong stream (dynamic vs unified)
- Parameters not being accessed correctly
- Navigation logic has errors
Solutions:
- Verify stream listeners are set up after SDK initialization
- Check you're listening to the correct stream:
dynamicLinkStream/onDynamicLinkfor dynamic linksunifiedLinkStream/onUnifiedLinkfor unified links
- Add debug logging to see what data is received
- Check navigation code for errors
Issue: Links Work on Simulator/Emulator But Not Device
Possible Causes:
- Domain association not trusted on device
- App not reinstalled after configuration changes
- Cached association data
Solutions:
- Delete and reinstall the app (forces re-verification)
- Clear app data and cache
- For iOS: Reset network settings
- For Android: Force re-verification with ADB
Issue: Cold Start Links Not Working
Possible Causes:
- Not checking for initial links on app launch
persistLastLinkDatadisabled- Initial link check happens too late
Solutions:
- Always check
getInitialDeepLink()when app starts - Enable
persistLastLinkDatain your config - Check for initial links before setting up stream listeners
Issue: Analytics Show Clicks But App Doesn't Receive Links
Possible Causes:
- Wrong stream subscription
- Links opening in browser instead of app
- Domain/configuration mismatch
Solutions:
- Verify you're subscribed to both dynamic and unified streams
- Check that links are actually opening the app (not browser)
- Verify domain in dashboard matches manifest/Info.plist configuration
- Check logs to see if links are being processed
4. Verify iOS Universal Links
Universal links require proper domain association. ULink automatically serves the Apple App Site Association (AASA) file for you - you don't need to host it yourself. Follow these steps to verify your configuration.
Step 1: Check the AASA File
ULink serves the AASA file automatically at your domain. Verify it's accessible:
curl https://<your-domain>/.well-known/apple-app-site-association
Expected Output:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.yourapp",
"paths": [
"NOT /__/auth/action/",
"NOT /__/auth/handler/",
"NOT /_/*",
"/*"
]
}
]
},
"webcredentials": {
"apps": ["TEAM_ID.com.yourapp"]
}
}
Common Issues:
- 404 Not Found: Domain not verified in ULink dashboard or not configured correctly
- HTML Response: Domain may not be pointing to ULink's servers correctly
- Missing Bundle ID: iOS configuration (Bundle ID and Team ID) not set in ULink dashboard
- Empty details array: iOS app configuration missing in Configuration → General → iOS
ULink automatically generates and serves the AASA file for your domain. You don't need to:
- Host the file yourself
- Deploy it to your server
- Configure your web server
The file is generated based on your iOS configuration in the ULink dashboard:
- Bundle ID: Set in Configuration → General → iOS → Bundle Identifier
- Team ID: Set in Configuration → General → iOS → Team ID
If the file is not accessible, check:
- Domain is verified in Domains section of dashboard
- Domain DNS is pointing to ULink's servers
- iOS configuration is complete in dashboard
Step 2: Test Link Association
xcrun simctl openurl booted "https://<your-domain>/<slug>"
Expected Behavior:
- App opens immediately (no disambiguation dialog)
- Link is processed and streams emit data
If App Doesn't Open:
- Check Associated Domains in Xcode (Signing & Capabilities)
- Verify domain format:
applinks:<your-domain>(nohttps://) - Ensure domain matches exactly what's in dashboard
Step 3: Monitor Association Logs (Physical Device)
On a physical device, monitor association logs:
log stream --predicate 'subsystem == "com.apple.applinks"' --style compact
Then tap your link and watch for:
accepted- Link association is trusteddenied- Link association failed
If You See "denied":
- Verify Associated Domains capability is added in Xcode
- Check domain format in Xcode matches dashboard exactly
- Verify domain is verified in ULink dashboard
- Ensure iOS configuration (Bundle ID, Team ID) is set in dashboard
- Delete and reinstall app (forces re-verification)
- Verify AASA file is accessible via curl (see Step 1)
Step 4: Verify Xcode Configuration
- Open your project in Xcode
- Select your target → Signing & Capabilities
- Verify Associated Domains capability is added
- Check domain entries:
- Format:
applinks:yourdomain.com(nohttps://) - Must match domain in ULink dashboard exactly
- No trailing slashes or paths
- Format:
5. Verify Android App Links
Android App Links require Digital Asset Links verification. ULink automatically serves the Asset Links file for you - you don't need to host it yourself. Follow these steps.
Step 1: Check Digital Asset Links JSON
ULink serves the Asset Links file automatically at your domain. Verify it's accessible:
curl https://<your-domain>/.well-known/assetlinks.json
Expected Output:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourapp",
"sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"]
}
}]
Common Issues:
- 404 Not Found: Domain not verified in ULink dashboard or not configured correctly
- HTML Response: Domain may not be pointing to ULink's servers correctly
- Wrong Fingerprint: SHA-256 fingerprint doesn't match what's configured in dashboard
- Missing Package: Android configuration (Package Name) not set in ULink dashboard
- Empty array: Android app configuration missing in Configuration → General → Android
ULink automatically generates and serves the Asset Links file for your domain. You don't need to:
- Host the file yourself
- Deploy it to your server
- Configure your web server
The file is generated based on your Android configuration in the ULink dashboard:
- Package Name: Set in Configuration → General → Android → Package Name
- SHA-256 Fingerprints: Set in Configuration → General → Android → SHA-256 Certificate Fingerprints
If the file is not accessible, check:
- Domain is verified in Domains section of dashboard
- Domain DNS is pointing to ULink's servers
- Android configuration is complete in dashboard
- SHA-256 fingerprints match your app signing key exactly
Step 2: Use Google's Asset Links Tool
Verify your domain association using Google's Asset Links Tool:
- Enter your domain
- Enter your package name
- Click "Test Statement"
- Verify it shows as "Verified"
Step 3: Inspect App Links Status with ADB
Use ADB commands to check verification status (replace com.example.app with your package name):
# Show current status for each host
adb shell pm get-app-links com.example.app
Expected Output:
com.example.app:
ID: 1234567890abcdef
Signatures: [abc123...]
Domain verification state:
links.shared.ly: verified
yourdomain.com: verified
Status Values:
verified- Domain is properly associated ✅approved- User manually approved (shows disambiguation dialog)none- Not verified, will show disambiguation dialoglegacy_failure- Verification failedno_response- No response from server
Step 4: Force Re-verification
If status shows none or legacy_failure, force re-verification:
# Force Android to re-check Digital Asset Links
adb shell pm verify-app-links --re-verify com.example.app
# Wait a few seconds, then check status again
adb shell pm get-app-links com.example.app
Step 5: Test the Link
After verification succeeds, test the link:
adb shell am start \
-a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE \
-d "https://<your-domain>/<slug>"
Expected Behavior:
- App opens immediately (no disambiguation dialog)
- Link is processed and streams emit data
Troubleshooting Android App Links
Status Shows "none" or "legacy_failure"
-
Check Digital Asset Links JSON:
curl https://<your-domain>/.well-known/assetlinks.jsonVerify it contains your package name and correct SHA-256 fingerprint. If it returns 404 or empty array, the domain may not be verified or Android configuration is missing in dashboard.
-
Verify Domain Configuration in Dashboard:
- Confirm domain is listed and verified in Domains section
- Check domain DNS is pointing to ULink's servers
- Verify domain status shows as "Verified"
-
Verify Android Configuration in Dashboard:
- Go to Configuration → General → Android
- Ensure Package Name matches your app's package name exactly
- Verify SHA-256 Certificate Fingerprints are set and correct
- Check that fingerprints match your app signing key
-
Check SHA-256 Fingerprint:
# Debug keystore
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# Release keystore
keytool -list -v -keystore your-release-key.keystore -alias your-aliasEnsure the SHA-256 matches what's configured in the ULink dashboard (not just what's in the JSON, since ULink generates it from dashboard config)
-
Re-run Verification:
adb shell pm verify-app-links --re-verify com.example.app
App Opens But Shows Disambiguation Dialog
This means App Links are not verified. The user must manually select your app. To fix:
- Follow the verification steps above
- Ensure
android:autoVerify="true"is set in your intent filter - Verify Digital Asset Links JSON is correct
6. Platform-Specific Debugging
iOS Debugging Checklist
- AASA file accessible and returns valid JSON
- Associated Domains capability added in Xcode
- Domain format correct:
applinks:yourdomain.com - Domain matches ULink dashboard configuration
- App reinstalled after configuration changes
- URL handling methods implemented in AppDelegate
- Stream listeners set up and active
- Debug logs show link processing
Android Debugging Checklist
- Asset Links JSON accessible and returns valid JSON
- SHA-256 fingerprint matches signing key
- Intent filter has
android:autoVerify="true" - Domain in intent filter matches dashboard
- App Links status shows
verifiedin ADB - Stream listeners set up in coroutine scope
- Debug logs show link processing
- App reinstalled after configuration changes
Flutter Debugging Checklist
- Native platforms configured (iOS and Android separately)
- SDK initialized before setting up listeners
-
DEBUG: Link listeners setup completeappears in logs - Stream subscriptions active (not cancelled)
- Both
onDynamicLinkandonUnifiedLinklisteners set up - Initial link checked on app start
- Platform-specific issues resolved (see iOS/Android checklists)
7. Advanced Troubleshooting
Enable Maximum Debug Logging
iOS:
let config = ULinkConfig(
apiKey: "your-api-key",
debug: true // Enable debug logging
)
Android:
val config = ULinkConfig(
apiKey = "your-api-key",
debug = true // Enable debug logging
)
Flutter:
await ULink.instance.initialize(
ULinkConfig(
apiKey: 'your-api-key',
debug: true, // Enable debug logging
),
);
Test Link Resolution Directly
You can test if a link resolves correctly without opening the app:
iOS:
Task {
let url = URL(string: "https://links.shared.ly/your-slug")!
let resolved = await ULink.shared.processULinkUrl(url)
print("Resolved: \(resolved?.slug ?? "nil")")
}
Android:
lifecycleScope.launch {
val uri = Uri.parse("https://links.shared.ly/your-slug")
val resolved = ulink.resolveLink(uri.toString())
Log.d("ULink", "Resolved: ${resolved.data}")
}
Flutter:
final response = await ULink.instance.resolveLink('https://links.shared.ly/your-slug');
if (response.success) {
print('Resolved: ${response.data}');
}
Verify Stream Subscriptions
Ensure your stream listeners are actually receiving data:
iOS:
ULink.shared.dynamicLinkStream
.sink { data in
print("✅ Received dynamic link: \(data.slug ?? "N/A")")
}
.store(in: &cancellables)
Android:
lifecycleScope.launch {
ulink.dynamicLinkStream.collect { data ->
Log.d("ULink", "✅ Received dynamic link: ${data.slug}")
}
}
Flutter:
_sdk.onDynamicLink.listen((data) {
debugPrint('✅ Received dynamic link: ${data.slug}');
});
8. Still Stuck?
If you've gone through all the troubleshooting steps and links still don't work, gather this information:
Information to Collect
-
Link Details:
- The exact link URL you're testing
- Link slug from ULink dashboard
- Link type (dynamic or unified)
-
Platform Information:
- Platform (iOS/Android/Flutter)
- OS version
- Device model
- App version
-
Configuration:
- Domain configured in dashboard
- Bundle ID / Package name
- URL scheme (if using custom schemes)
-
Logs:
- Relevant console/logcat output
- SDK initialization logs
- Link processing logs (if any)
- Error messages
-
Test Results:
- What happens when you tap the link?
- Does the app open?
- Do streams emit data?
- Any error messages?
Get Help
Email support@ulink.ly with:
- Subject: "Deep Link Troubleshooting - [Your Issue]"
- All the information collected above
- Screenshots of relevant logs or configurations
We'll review your configuration and help you get links working!