> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> SynheartAuthError taxonomy, key invalidation detection, and clock skew correction

The SDK exposes a sealed `SynheartAuthError` hierarchy in Dart (mirrored as enum-style errors in Kotlin and Swift). Every error has a stable code that maps 1:1 to RFC-AUTH-MOBILE-0001 §11.

## Taxonomy

| Code                       | Dart class                         | Trigger                                            | SDK behaviour                                                         |
| -------------------------- | ---------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------- |
| `NETWORK_ERROR`            | `NetworkError(message)`            | Challenge or register HTTP call failed             | Retry with backoff (RFC §12.1).                                       |
| `CHALLENGE_EXPIRED`        | `ChallengeExpired`                 | Server rejects stale challenge                     | Re-fetch a new challenge.                                             |
| `ATTESTATION_UNAVAILABLE`  | `AttestationUnavailable`           | Device/OS doesn't support attestation              | Surface to developer; cannot proceed.                                 |
| `ATTESTATION_FAILED`       | (subset of `ServerError`)          | Attestation provider rejects                       | Retry once; then surface.                                             |
| `KEY_INVALIDATED`          | `KeyInvalidated`                   | Hardware key no longer usable                      | Transition to `keyInvalid`; next `registerDevice` wipes and restarts. |
| `KEYSTORE_ERROR`           | `StorageError`                     | Android Keystore op failed                         | Surface with platform details.                                        |
| `SECURE_ENCLAVE_ERROR`     | `StorageError`                     | iOS Secure Enclave op failed                       | Surface with platform details.                                        |
| `SIGNING_FAILED`           | `CryptoError`                      | Signature computation failed                       | Return immediately; non-recoverable in-request.                       |
| `DEVICE_REVOKED`           | `ServerError`                      | Server marks device inactive                       | Force re-registration via `resetDeviceIdentity`.                      |
| `CLOCK_SKEW`               | `ClockSkew`                        | Timestamp freshness rejected                       | Apply offset correction; retry.                                       |
| `ROTATION_FAILED`          | `ServerError`                      | Key rotation request rejected                      | Keep old key active; retry later.                                     |
| `NONCE_REPLAY`             | `ServerError`                      | Server rejects duplicate nonce                     | Generate new nonce and retry once.                                    |
| `ALREADY_REGISTERED`       | `AlreadyRegistered`                | `registerDevice` called when state is `registered` | No-op fast-path; treat as success.                                    |
| `NOT_REGISTERED`           | `NotRegistered`                    | `signRequest` called before registration           | Call `registerDevice` first.                                          |
| `NOT_CONFIGURED`           | `NotConfigured`                    | API used before `configure(baseUrl)`               | Call `configure` once at app launch.                                  |
| `REGISTRATION_IN_PROGRESS` | `RegistrationInProgress`           | Concurrent `registerDevice`                        | Wait for the in-flight call.                                          |
| `CRYPTO_ERROR`             | `CryptoError(message)`             | Generic crypto failure                             | Inspect message; usually surfaced to developer.                       |
| `STORAGE_ERROR`            | `StorageError(message)`            | Keychain / EncryptedSharedPreferences failure      | Inspect message; surface.                                             |
| `INVALID_STATE_TRANSITION` | `InvalidStateTransition(from, to)` | Programmer error; state machine misuse             | Inspect call site.                                                    |
| (any other server code)    | `ServerError(code, serverMessage)` | Catch-all for server-defined codes                 | Inspect; default surface to developer.                                |

`SynheartAuthError.fromCode(code, msg?)` maps platform-channel error codes back to the typed exception in Dart.

## Key invalidation detection (RFC §11.1)

Hardware keys can become unusable due to:

* OS updates that rotate Secure Enclave / Keystore root keys.
* Biometric enrollment changes.
* Device security state changes (PIN reset, factory reset).
* Backup/restore between devices.

| Platform | Detection                                                                                                                                                                                                                             |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android  | `KeyPermanentlyInvalidatedException` on `Cipher.init`/`Signature.initSign`. `UnrecoverableKeyException` when the alias no longer resolves.                                                                                            |
| iOS      | `errSecItemNotFound` from `SecKeyCopyExternalRepresentation` or `SecKeyCreateSignature`. `errSecAuthFailed` when the biometric context can't authenticate. `LAError.biometryLockout` for biometry-bound keys after too many failures. |

On detection:

1. The SDK emits `KEY_INVALIDATED` (Dart: `KeyInvalidated`).
2. State transitions `registered → keyInvalid`.
3. The next `registerDevice(appId)` call wipes local state and runs the full registration flow.

The SDK never silently retries with the invalid key.

## Clock skew correction (RFC §11.2)

Mobile clocks drift. The SDK can't force NTP, so it learns the offset from the server.

### Server contract

When rejecting a request with `CLOCK_SKEW`, the response includes:

```json theme={null}
{
  "error": "CLOCK_SKEW",
  "server_timestamp": 1709312345,
  "message": "Request timestamp too far from server time"
}
```

### SDK behaviour

```dart theme={null}
Future<void> correctClockSkew(double serverTimestamp) {
  final localTimestamp = DateTime.now().millisecondsSinceEpoch / 1000;
  final offsetMs = ((serverTimestamp - localTimestamp) * 1000).round();
  // persist offsetMs as clock_offset_ms
  // on next sign: adjusted_ts = local_ts + clock_offset_ms / 1000
}
```

The SDK exposes `correctClockSkew(serverTimestamp)` for hosts to call when they receive a `CLOCK_SKEW` error from any Synheart endpoint. Subsequent signatures use the corrected timestamp automatically.

The persisted `clock_offset_ms` survives app restarts. Per RFC §11.2, the SDK SHOULD periodically re-validate the offset by comparing with `X-Synheart-Server-Time` from successful responses (server-supplied; not currently emitted by all services).

## Retry policy (RFC §12)

### Challenge + registration

```text theme={null}
delay = min(base * 2^attempt + random(0, jitter), max_delay)
base   = 1 second
jitter = 500 ms
max    = 30 seconds
attempts = 5
```

Always re-fetch a new challenge after any register failure (challenges are single-use).

### Signing

Signing failures are non-recoverable in-request — return error immediately. If `KEY_INVALIDATED` is detected, transition to `keyInvalid` and trigger re-registration before the next request.

### Key rotation

Rotation failures MUST NOT block normal request signing. Backoff: base 60s, max 1 hour, attempts 3 per cycle. After max attempts, log warning and retry on next app launch.

## Logging policy

Per RFC §13:

| Allowed (prod)                 | Never (prod)                                        |
| ------------------------------ | --------------------------------------------------- |
| `app_id`                       | Raw signatures                                      |
| `device_id` (truncated/hashed) | Raw request bodies                                  |
| Error codes                    | Attestation proof payloads                          |
| Timing metrics                 | Public key material (debug builds only with opt-in) |
| Rotation events                | Clock offset values                                 |
| State transitions              |                                                     |

`InvalidStateTransition` should be logged at error level — it indicates a programmer bug.

## Related

* [State Machine](/synheart-auth/state-machine) — `KeyInvalidated` and `InvalidStateTransition` semantics.
* [Registration](/synheart-auth/registration) — failure paths during the handshake.
* [Signing](/synheart-auth/signing) — `CLOCK_SKEW`, `NONCE_REPLAY` recovery.
