# Troubleshooting & FAQ

Indexed by the symptom you see. Each entry gives the cause and the fix.

---

## 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.

**Fix.** Run on a real device. It will never work on a simulator or emulator, and waiting will not help.

The underlying tamper signals (mock-location flag, jailbreak / root indicators, GNSS anomalies, attestation results) are **deliberately not** surfaced in `verdict.message`. Exposing which signal fired is a security risk: it tells an attacker exactly what to defeat.

---

### `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](https://github.com/octetproof/octet-sdk-ios/blob/main/CHANGELOG.md) and the [`octet-sdk-android` CHANGELOG](https://github.com/octetproof/octet-sdk-android/blob/main/CHANGELOG.md).

---

## 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`

The cached activation is past the offline grace and the SDK could not reach the backend to refresh. Age alone no longer expires a maintained key.

**Fix.** Reconnect so the SDK can refresh the activation. If it persists, contact support.

---

### `LicenseError.ActivationWindowClosed`

Not returned under the current maintained-license model. Octet maintains active licenses, so there is no activation window to close.

**Fix.** If you hit this, contact support.

---

### `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 issues

### 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](/docs/getting-started/prerequisites/).

---

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

Background-location requires both:

```xml
<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

- [Verdicts](/docs/concepts/verdicts/). Full `ReasonCode` taxonomy.
- [License & Activation](/docs/concepts/license-activation/). Timeline model and activation flow.
- [License Types API Reference](/docs/api-reference/license-types/). Exact `LicenseError` cases.
