Testing & Troubleshooting
Deferred flow testing has platform constraints: full attribution path depends on store redirect chain.
Deferred flow constraints
Section titled “Deferred flow constraints”- Deferred matching is first-launch oriented.
- Runtime callbacks do not re-trigger deferred matching logic.
- The Flutter bridge does not expose a session reset API.
Recommended deferred test flow
Section titled “Recommended deferred test flow”- Uninstall the app from the device or simulator.
- Open a Detour link.
- Install and launch the app.
- Verify the initial intent and the resolved route in your UI logs.
- Relaunch the app and confirm the deferred behavior is not repeated.
Runtime link tests
Section titled “Runtime link tests”Android App Link
Section titled “Android App Link”adb shell am start -a android.intent.action.VIEW \ -d "https://<your-link-domain>/<link-token>" \ com.example.appAndroid custom scheme
Section titled “Android custom scheme”adb shell am start -a android.intent.action.VIEW \ -d "myapp://product/42" \ com.example.appiOS custom scheme (simulator)
Section titled “iOS custom scheme (simulator)”xcrun simctl openurl booted "myapp://product/42"iOS Universal Link (simulator)
Section titled “iOS Universal Link (simulator)”xcrun simctl openurl booted "https://<your-link-domain>/<link-token>"Platform-specific notes
Section titled “Platform-specific notes”Android
Section titled “Android”Install referrer click_id is often unavailable in local/dev-store scenarios. In that case matching falls back to probabilistic flow.
For local install referrer testing workarounds, see How to test the Play Store Install Referrer API.
Testing Android App Links
Section titled “Testing Android App Links”If an installed app does not open Detour links, the Dashboard SHA-256 probably does not match the key that signed the build on your device:
| How you install the app | SHA-256 to use | Where to find it |
|---|---|---|
| Google Play, any track | Play App Signing key | Play Console → Protected with Play → Manage Play app signing → App signing key certificate |
flutter run / flutter build apk --debug | AGP default debug keystore | keytool -list -v -keystore ~/.android/debug.keystore -storepass android, or ./gradlew signingReport |
| Release build with your own keystore | Your release keystore | keytool -list -v -keystore <path-to-your.keystore> |
The Flutter template signs release with the debug config until you add your own release keystore, so a fresh flutter build apk --release is still debug-signed.
To check any build directly: apksigner verify --print-certs build/app/outputs/flutter-apk/app-debug.apk. The complete procedure is described in Finding your Android SHA-256 fingerprint.
Common issues
Section titled “Common issues”No match found (404)
- Check timestamps. The click may be older than the match window (15 minutes).
- Confirm the click was recorded in your Detour Dashboard.
- On the Android deterministic flow, confirm
click_idis present in the install referrer.
App/Universal link never arrives despite a valid click
Your plan’s click limit may be exceeded. The plugin surfaces nothing in Dart, because the intent never arrives, so check the native log for [Detour:CLICK_LIMIT_ERROR]. See Click limits.
429 Too Many Requests
Detour limits deferred link matching per device and IP address, and per app. Wait between reinstall tests on one device. For tests on many devices at once, create a separate app for testing in the Dashboard, because each app has its own credentials and its own limit.
NOT_CONFIGURED platform error
resolveInitialLink() and processLink() raise it on both platforms. On
Android, logEvent() and logRetention() raise it too.
Cause — the method ran before a successful configure(...) call.
Fix — await DetourService.start(...) or
DetourFlutterPlugin.configure(...) before you call these methods. If
configure(...) failed, fix that error first.
INVALID_ARGS platform error
Android only. configure(...) raises it when apiKey or appID is empty,
processLink(...) when url is empty, and logRetention(...) when eventName
is empty. A value that contains only whitespace counts as empty.
Cause — a required string argument is empty.
Fix — pass non-empty values. For where apiKey and appID come from, see
Credentials.
NO_ACTIVITY platform error
Cause — on Android, configure(...) ran in a Flutter engine that has no
attached activity, such as a separate engine started for background work.
Fix — call DetourService.start(...) or
DetourFlutterPlugin.configure(...) from your app’s UI code, not from a
background handler.
NATIVE_ERROR platform error
Android only. resolveInitialLink() and processLink() raise it, and
linkStream emits it as an error event, when the native SDK fails to process a
link. DetourService.start() throws the same error and leaves
isInitialLinkProcessed at false, so catch it if your startup flow waits for
that flag.
A failed deferred match request on first launch raises it. When the server responded, the message starts with the HTTP status code:
[401]— theapiKeyorappIDis wrong. See Credentials.[402]— your organization is over its plan’s click limit. See Click limits.[429]— too many match requests. See429 Too Many Requests.- No status code — the request did not complete. Check the network connection.
iOS clipboard not read
- Ensure
shouldUseClipboard: trueis set inDetourConfig. - Ensure the copy-link feature is enabled in the Detour Dashboard for your iOS app configuration.
- The link must be copied to the clipboard before the app is opened, because the clipboard is only read during
resolveInitialLink().
Deferred link not returned on subsequent test runs
Deferred matching is first-launch based. Reinstall the app for a clean re-test.
Android release build fails with Type com.swmansion.detour.BuildConfig is defined multiple times
Debug builds of the same project succeed.
Cause — detour_flutter_plugin 1.1.1 and 1.2.0 use the Android
namespace com.swmansion.detour, which the native Detour SDK also uses. The
release build then contains two classes named
com.swmansion.detour.BuildConfig.
Fix — upgrade to detour_flutter_plugin: ^1.2.1 and run flutter pub get.
Debugging
Section titled “Debugging”- Android:
adb logcat | grep -i detour
- iOS simulator:
xcrun simctl spawn booted log stream --predicate 'subsystem == "com.swmansion.detour"'
For full runnable setup and commands, use Examples.