Troubleshooting¶
getClassification() always returns insufficient or undetermined¶
Cause: One of the following:
- The user has not consented, so nothing is recorded.
- The session has fewer actions than the model asks for, 8 with the built-in model: the result
is
insufficient. See Evidence. - The result is
undetermined: the score lies betweenbotThresholdandhumanThreshold. - Capture was paused and never resumed.
- The SDK cannot score:
isScoringisfalseand the result isinsufficient. - The user's input happened where the SDK cannot see it — typing, or a web view — and your app does not report it.
- The app came back after more than five minutes in the background, and the evidence started over.
Solution: Check each in turn:
final dea = DeaSdk.instance!;
debugPrint('consent: ${dea.consent.decision}'); // must be granted
debugPrint('scoring: ${dea.isScoring}'); // must be true
debugPrint('error: ${dea.error}'); // must be null
final read = dea.getClassification();
debugPrint('result: ${read.result}, actions: ${read.actions}, '
'score: ${read.value}, thresholds: ${read.botThreshold}..${read.humanThreshold}, '
'model: ${read.modelVersion}');
insufficientmeans fewer actions than the model asks for yet:actionsis below 8 with the built-in model, andscoreisnull.undeterminedmeans the score lies between the two thresholds.- Make sure every
pause()has a matchingresume(). - For input the SDK cannot see, see Report input the SDK cannot see.
getClassification() throws ThresholdMissing¶
Cause: The loaded model has no bot threshold. Its version is on the exception's
modelVersion.
Solution: Catch ThresholdMissing at the decision point and take your fallback path. Make sure
model updates are running — modelDirectory passed, modelUpdates left on — so the SDK replaces
the model when DEA publishes a newer one; checkForModel(force: true) checks at once. See
Options.
getClassification().actions stays at 0 while the user interacts¶
Cause: Nothing is being recorded. Usually the user has not consented yet; otherwise capture is
paused, or events excludes the input you are testing.
Solution: Show the consent dialog and accept it — see
User Consent. If consent is granted, check for a
pause() without a matching resume(), and check events.
Nothing appears in the log with debug: true¶
Cause: The published Flutter package carries no log. debug and onLog have no effect in it.
Solution: Check the SDK from code instead — see Verify your setup.
error is a CoreMismatch and isScoring is false¶
Cause: The scoring engine is not running. error says which of these it is:
dea_sdkis at a different version fromdea_baseordea_automation_detection. The SDK refuses to run with mixed versions and stays paused.- The scoring engine could not be loaded on this device. The package ships it for Android
arm64-v8aandx86_64, so a device that can only run 32-bit apps (armeabi-v7a) has none.
Solution: For mixed versions, download all three zips from the same release, and point
dea_base and dea_automation_detection at those copies:
dependencies:
dea_sdk:
path: vendor/dea/sdk
dependency_overrides:
dea_base:
path: vendor/dea/base
dea_automation_detection:
path: vendor/dea/automation_detection
Then run flutter pub get.
For a library that could not be loaded, test on a 64-bit device or emulator. On a device without
the scoring engine the SDK keeps running, and getClassification() answers insufficient.
Options passed to a later init are ignored¶
Cause: DeaSdk.init was called more than once. The first call's options stay in effect.
Solution: Call DeaSdk.init once, in main(). If you really need different options, call
DeaSdk.instance?.dispose() first.
The consent dialog appears on every launch¶
Cause: The answer is not being stored. Either remember: false is passed, or the SDK's native
plugin is not registered in the app (for example, in an add-to-app setup), so the answer cannot be
saved.
Solution: Remove remember: false unless the device is shared. Otherwise make sure the app
uses the generated plugin registrant, and rebuild the app fully rather than hot-restarting after
adding the package.
attest() returns unsupported¶
Cause: One of the following:
- No
appKeywas passed toDeaSdk.init.attestation.detailthen says so. - No signing key could be created: the SDK's native plugin is not registered, or the device has no secure key storage.
Solution: Pass the appKey DEA issued for this platform's app — see
Options. Otherwise rebuild the app fully after adding the
package, and test on a real device.
attest() returns failed¶
Cause: attestation.detail says which of these it is:
- Google or Apple would not issue a token.
detailstarts withPlay Integrity refusedorApp Attest. - DEA refused the registration.
detailnames the request and the status DEA answered, for example/api/attest answered 403. - DEA could not be reached.
- Attestation is paused after earlier refusals.
detailstarts withattestation paused until.
Solution:
- On Android, test with a build installed from a Google Play testing track, on a device with Google Play services, and check that your app's Play Console entry is linked for Play Integrity — see Attest.
- On iOS, a
detailmentioninginvalidKeyusually means the App Attest capability is missing from the build — see Attest. - Check that the
appKeyis the one DEA issued for this platform's app: the Android key on Android, the iOS key on iOS. - A paused attestation resumes by itself at the time
detailgives.
attest() passes but platformAttested is false¶
Cause: The platform had no token to give: the app runs on the iOS Simulator, or on iOS 12 or 13, where App Attest is not available.
Solution: Test attestation on a real device with iOS 14 or later — see Attest.
Touches inside a web view do not count¶
Cause: A web view handles its own touches, and Flutter does not route them to the SDK.
Solution: Forward them with capturePointer — see
Report touches inside a web view.
If forwarded touches still do not count, check that their kinds are listed in
events and that x and y include the web view's offset in
the window.