Skip to content

Troubleshooting

getClassification() always returns insufficient or undetermined

Cause: One of the following:

  1. The user has not consented, so nothing is recorded.
  2. The session has fewer actions than the model asks for, 8 with the built-in model: the result is insufficient. See Evidence.
  3. The result is undetermined: the score lies between botThreshold and humanThreshold.
  4. Capture was paused and never resumed.
  5. The SDK cannot score: isScoring is false and the result is insufficient.
  6. The user's input happened where the SDK cannot see it — typing, or a web view — and your app does not report it.
  7. 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}');
  • insufficient means fewer actions than the model asks for yet: actions is below 8 with the built-in model, and score is null.
  • undetermined means the score lies between the two thresholds.
  • Make sure every pause() has a matching resume().
  • 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:

  1. dea_sdk is at a different version from dea_base or dea_automation_detection. The SDK refuses to run with mixed versions and stays paused.
  2. The scoring engine could not be loaded on this device. The package ships it for Android arm64-v8a and x86_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.

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:

  1. No appKey was passed to DeaSdk.init. attestation.detail then says so.
  2. 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:

  1. Google or Apple would not issue a token. detail starts with Play Integrity refused or App Attest.
  2. DEA refused the registration. detail names the request and the status DEA answered, for example /api/attest answered 403.
  3. DEA could not be reached.
  4. Attestation is paused after earlier refusals. detail starts with attestation 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 detail mentioning invalidKey usually means the App Attest capability is missing from the build — see Attest.
  • Check that the appKey is 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 detail gives.

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.