Skip to main content

Troubleshooting & FAQ

Indexed by the symptom you actually see. Each entry says what the SDK is telling you, why, and what to change.


Verdict reasons

INDETERMINATE / NO_FIX on emulator or simulator

result: INDETERMINATE
reason: NO_FIX
message: running on simulator — location proofs are unavailable in this environment
(Android: "running on emulator — …")

Why. The simulator (iOS) has no real GNSS or motion stack. The Android emulator's "telnet geo fix" provider sets Location.isMock = true. Either way the SDK's spoof-detection pipeline blocks proof generation. The ring buffer never fills.

Fix. Run on a real device. This will never start working in a simulator or emulator. The pipeline is not "warming up".

The underlying tamper signals (mock-location flag, jailbreak / root indicators, GNSS anomalies, attestation results) are deliberately not surfaced in verdict.message. Revealing which signal fired would help an attacker tune around it.


INDETERMINATE / NO_FIX on a real device

The SDK started but has not seen a usable fix yet, or every fix so far has been rejected by spoof detection.

Fix.

  • Indoors with no cellular? A country query usually succeeds anyway on Android (MCC from the serving cell). On iOS the SDK is more dependent on GNSS. Step outside.
  • GPS disabled at the OS level? Re-enable in Settings → Location.
  • Mock location app running? The OS flags every fix as mocked, and the SDK rejects them. Disable mock-location.
  • Just launched? Give it a few seconds. On Android the first MCC-anchored country proof typically lands quickly. On iOS the first GNSS-anchored proof can take 10 to 30 seconds outdoors.

INDETERMINATE / STALE_FIX

atTime falls outside the validity window of any cached proof.

Fix.

  • You're querying a past time. If you need historical answers, query within a few minutes of when the user was actually there. The per-resolution windows are: country ±5 min, subdivision ±3 min, city ±1 min, finer shapes ±30 s.
  • You're querying near-now. Wait briefly. The SDK is likely producing a fresh proof on this exact call. If the indeterminate persists, see NO_FIX above.

INDETERMINATE / FUTURE_TIME

atTime > now + 2 s (the SDK's clock-skew tolerance).

Fix. Do not query the future. If you are seeing this with atTime: Date() or Instant.now() exactly, your device clock is significantly skewed relative to the SDK's internal clock. Re-sync the device clock.


INDETERMINATE / NO_PROOF_AT_RESOLUTION

The cached proof is too coarse for the query. For example, you asked isWithin(city("San Francisco")) and the most recent proof is at country level.

Fix. The SDK should regenerate at the finer level on the next live query. If it persists indoors with no GPS, you may be in an environment where the finer resolution is not provable. Fall back to a coarser predicate (subdivision instead of city) or accept the indeterminate result.


INDETERMINATE / MOCK_LOCATION_DETECTED

The OS flagged the fix as mocked. The SDK refuses to issue proofs for mocked fixes.

Fix. Disable any mock-location apps. On Android, also check Developer Options → "Select mock location app" and clear it.


INDETERMINATE / ATTESTATION_FAILED

Apple App Attest (iOS) or Google Play Integrity (Android) returned a non-compliant verdict.

Fix. This usually means the device or build environment failed integrity checks: rooted device, custom ROM, modified APK or IPA, debugger attached in a way the attestation provider rejects. The SDK cannot override this. The device or build needs to pass platform integrity.


INDETERMINATE / SDK_NOT_RUNNING

You called a predicate before Octet.start(...) completed, or after the SDK session ended.

Fix. Hold the OctetSdk handle that start returned and call predicates on it. Do not call into a nil or disposed reference.


INDETERMINATE / NOT_YET_RELEASED

The predicate path is declared by the v1 API but not yet wired in this SDK build (e.g. some shape-pair containment combinations).

Fix. Until the feature lands, fall back to a supported predicate. The combinations that currently produce NOT_YET_RELEASED are documented per release in the octet-sdk-ios CHANGELOG and the octet-sdk-android CHANGELOG.


License errors

LicenseError.MalformedKey

The PASETO signature didn't verify, the prefix is unknown, the typ claim is wrong, or a required claim is missing.

Fix. Re-copy the key from the signup email. Make sure you copy the entire token including the octet_live_ or octet_test_ prefix.


LicenseError.NoActivation

No cached activation token, and the device is offline.

Fix. Connect to a network long enough for one successful activation. After that, the SDK works offline for up to 7 days per cached token.


LicenseError.Expired

Either the license is past day 105 (hard stop), or the cached activation is past the 7-day offline grace.

Fix. Request a new license key. Old app builds carrying the old key stop working at the cutoff. Ship an app update with the new key.


LicenseError.ActivationWindowClosed

The license is past its 90-day activation window. Already-activated devices keep working. This device cannot be added as a new activation.

Fix. Request a new license key. Already-activated devices continue normally.


LicenseError.Revoked

The license was revoked by an Octet admin (typical reasons: key was leaked, abuse, payment dispute).

Fix. Contact Octet support.


LicenseError.Network

Transient network failure during activation. The associated message (iOS) or cause (Android) carries the underlying error for diagnostics. Do not parse it for control flow.

Fix. Retry. If a usable cached activation exists, the SDK never falls into this path on subsequent launches.


LicenseError.ServerRejected(httpStatus, reason)

Backend rejected for a reason we do not have a dedicated case for (e.g. app_blocked, ip_blocked).

Fix. The reason tag identifies the policy that fired. Contact support for app_blocked or ip_blocked. For other tags, check the release notes of the SDK version you are on.


Platform-specific gotchas

iOS: app crashes on launch with "privacy-sensitive data" error

Missing Info.plist key. The error message names the missing key, usually NSMotionUsageDescription or NSLocationWhenInUseUsageDescription. See Prerequisites.


iOS: SDK works in foreground but stops generating proofs in background

Background-location requires both:

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string></string>
<key>UIBackgroundModes</key>
<array><string>location</string></array>

AND the user must grant .authorizedAlways (not just .authorizedWhenInUse). The SDK silently falls back to foreground-only if either is missing.


Android: SDK refuses to start with location permission granted in manifest

Manifest-level permission is necessary but not sufficient on Android 6 (API 23)+. You must request ACCESS_FINE_LOCATION at runtime and wait for the user to grant it before calling Octet.start(...).


Android: motion classification looks degraded

ACTIVITY_RECOGNITION was likely not granted (Android 10+ / API 29+). The SDK degrades gracefully but with lower confidence. Request the permission at runtime alongside location.


Both platforms: Octet.start(...) is slow on first launch

Expected on first launch: license signature verification, plus a network round-trip to /v1/activate, plus the proof pipeline bring-up. Subsequent launches reuse the cached activation token and skip the network call. If first-launch time matters, call start from a coroutine or task on app launch and gate UI on its completion.


See also