---
title: Error reference
description: Every typed error the Extentos SDK can return — ConnectError, CaptureError, AudioError, TransportError, the ExtentosError umbrella, and the Meta-DAT DeviceSessionError — with their payload fields and meaning. Lifecycle operations return ExtentosResult<T, E> with these concrete failure variants rather than throwing; pattern-match them. Generated from the Rust core.
type: reference
platform: all
related:
  - /docs/reference
  - /docs/sdk/android
  - /docs/troubleshooting
  - /docs/concepts/permissions
---

{/* EXTENTOS-GENERATED — do not hand-edit. Source: core/extentos-core/src/types/errors.rs
    + real_meta/mod.rs. Regenerate: cd mcp-server && npm run docs:errors. The
    AssistantError section is hand-authored (shell-level Phase-4 type). */}

> **Generated from source.** The error enums below are emitted from the Rust core (`core/extentos-core/src/types/errors.rs`), which is codegen'd into the Kotlin and Swift SDKs. The same variants exist on both platforms; Kotlin uses the PascalCase names shown, Swift the lowerCamelCase equivalents (`TransportFailure` → `.transportFailure`).

## How errors surface

Lifecycle operations don't throw — they return an **`ExtentosResult<S, F>`** with a typed failure variant. This is DAT's own convention and avoids coroutine-cancellation footguns. Pattern-match the result; **don't** wrap it in `runCatching` (which only catches thrown exceptions and would mask the typed error):

```kotlin
when (val r = glasses.camera.capturePhoto()) {
    is ExtentosResult.Ok  -> render(r.value)          // Photo
    is ExtentosResult.Err -> when (val e = r.error) {  // CaptureError
        is CaptureError.PermissionDenied -> requestCameraPermission()
        is CaptureError.ThermalCritical  -> toast("Glasses too hot — let them cool")
        is CaptureError.DisabledByUser   -> explainToggle(e.toggle)
        else                             -> log(e)
    }
}
```

Helpers on the Kotlin `ExtentosResult`: `.map { }`, `.flatMap { }`, `.valueOrNull()`, `.errorOrNull()`.

The Swift shape differs in names, not model — `ExtentosResult` is `.success(Success)` / `.failure(Failure)`, with `map` / `flatMap` and optional `success` / `failure` properties instead of `valueOrNull()` / `errorOrNull()`:

```swift
switch await glasses.camera.capturePhoto() {
case .success(let photo): render(photo)
case .failure(let error): // CaptureError
    switch error {
    case .permissionDenied: requestCameraPermission()
    case .thermalCritical:  toast("Glasses too hot — let them cool")
    default:                log(error)
    }
}
```

The `code` + `message` carried by the `PlatformError` / `TransportFailure` variants is the shell's stable machine tag for a native-origin failure (the original native exception is logged to the event log at the catch site, not carried across the FFI).

## Connection errors

### `ConnectError`

Returned by `glasses.connection.connect()`.

| Variant | Payload | Meaning |
|---|---|---|
| `NoDeviceAvailable` | — | No paired or otherwise known device is available to connect to. |
| `PermissionDenied` | — | A runtime permission required to connect was denied. |
| `PairingFailed` | — | Pairing / handshake with the device failed. |
| `RegistrationRequired` | — | The install must register / authenticate before it can connect. |
| `TransportFailure` | `code: String, message: String` | A native transport-layer failure surfaced while connecting. |

## Capture errors

### `CaptureError`

Returned by `glasses.camera.capturePhoto()` / `captureVideo()`.

| Variant | Payload | Meaning |
|---|---|---|
| `NotConnected` | — | — |
| `PermissionDenied` | — | — |
| `ThermalCritical` | — | — |
| `HingesClosed` | — | — |
| `CoexistenceBlocked` | — | — |
| `StreamPaused` | — | The user paused the camera with the glasses' capture button (temple tap), so the live stream this capture needs is held, not flowing. |
| `DisabledByUser` | `toggle: String` | A user-facing toggle is gating this capture; `toggle` is the toggle key (e.g. `camera_streaming_enabled`) so a handler can explain it. |
| `PlatformError` | `code: String, message: String` | A native-origin capture failure — structured `code` + `message`. |

## Audio errors

### `AudioError`

Returned by `glasses.audio.recordDiscrete()` / `speak()`.

| Variant | Payload | Meaning |
|---|---|---|
| `NotConnected` | — | — |
| `PermissionDenied` | — | — |
| `CoexistenceBlocked` | — | — |
| `DisabledByUser` | `toggle: String` | As `CaptureError.DisabledByUser` — a toggle (e.g. `audio_capture_enabled`). |
| `PlatformError` | `code: String, message: String` | — |

## Transport errors

### `TransportError`

Surfaced by the underlying transport (simulator WebSocket or Meta DAT). `WebsocketClosed.code` is the WebSocket close code.

| Variant | Payload | Meaning |
|---|---|---|
| `WebsocketClosed` | `code: i32, reason: Option&lt;String&gt;` | — |
| `HandshakeFailed` | `reason: String` | — |
| `SchemaMismatch` | `server_version: String, client_version: String` | — |
| `Timeout` | — | — |
| `HardwareUnavailable` | `reason: String` | — |
| `PlatformError` | `code: String, message: String` | — |

## The umbrella type

### `ExtentosError`

`ExtentosError` wraps the four per-area errors plus a catch-all, for APIs that surface any failure.

| Variant | Payload | Meaning |
|---|---|---|
| `Connect` | `inner: ConnectError` | — |
| `Capture` | `inner: CaptureError` | — |
| `Audio` | `inner: AudioError` | — |
| `Transport` | `inner: TransportError` | — |
| `Unexpected` | `message: String` | — |

## Meta DAT device-session errors

### `DeviceSessionError`

Surfaced by the Meta DAT bridge (`RealMetaTransport`) when starting a device session on real hardware.

| Variant | Payload | Meaning |
|---|---|---|
| `NoEligibleDevice` | — | — |
| `PlatformError` | `code: String, message: String` | — |

## Assistant errors (Phase 4)

The Phase-4 assistant runtime (`glasses.assistant.*`) uses a shell-level `AssistantError` type (not a Rust core enum) with the same six variants on both platforms: a Kotlin sealed class thrown wrapped in `AssistantException` (`com.extentos:glasses`, since 1.4.0 — see [SDK install](/docs/sdk/android/install)) and a Swift `AssistantError` enum in `GlassesCore` (lowerCamelCase cases — `.noApiKey`, `.providerError(code:message:)`).

| Variant | Payload | Meaning |
|---|---|---|
| `NoApiKey` | — | The Extentos managed gateway isn't reachable. Legacy name: there is no API key to supply — the assistant always runs on the managed gateway. Beta and production builds attest automatically (Play Integrity / App Attest) and need no setup. A sideloaded **development** build cannot attest, so it reaches the gateway one of two other ways — bound to a browser-simulator session, or through the dev-tier project-key lane. If you hit this on real hardware from a debug build, that binding is what's missing: see [how the gateway identifies a caller](/docs/concepts/ai-gateway#identity-how-the-gateway-knows-whos-calling). |
| `AlreadyActive` | — | An assistant session is already active (the runtime is singleton-active). |
| `SessionEnded` | — | `start()` was called on a session that has already stopped. |
| `NotReady` | — | `wake` / `sleep` / `say` / etc. called on an Idle or non-Active session. |
| `NetworkError` | `cause: Throwable` | A network failure reaching the gateway or provider. |
| `ProviderError` | `code, message` | The upstream AI provider returned an error. |

## There is no `DisplayError`

Display (`glasses.display.*`) has **no returned error type** — `show()` degrades gracefully and never throws (a transport with no display path simply no-ops). Display delivery problems surface as **runtime log events** on `glasses.runtime.events` (`display.video_delivery_failed`, `display.image_delivery_failed`, `display.video_error`) and on the `errors` chip in the simulator [event log](/docs/mcp-server/tools/simulation) (they are warn severity). See the [display capability](/docs/concepts/capabilities) docs.
