Skip to main content
The session SDK carries two error surfaces: in-stream SessionError events that close a running stream, and synchronous exception classes thrown for API misuse before/during a Stream<SessionEvent> is established. They are aligned across Dart, Kotlin, and Swift.

SessionErrorCode (in-stream)

Wire enum from session.proto:
SDK projection β€” wire string is the snake_case JSON value the platform layer ships: SessionErrorCode.fromString(value) (all three SDKs) parses the wire string. Unknown values throw ArgumentError in Dart.

SessionError event

SessionError always closes the stream. After it fires, the same sessionId can be started again with a fresh SessionConfig. message is human-readable and platform-specific. Hosts should log it but should not parse it for control flow β€” only code is stable.

Recovery paths

Dart synchronous exceptions

For misuse that doesn’t reach the engine, the Dart SDK throws:
These do not reach the event stream β€” they’re thrown directly from SynheartSession.startSession(...) and friends.

Distinguishing the two surfaces

Channel error swallowing

The Watch event channel listener treats channel transport errors as non-fatal β€” dropped frames during a brief disconnect are preferable to forcing the host to handle a SessionError(osTerminated) for every Bluetooth blip. Apps that need strict reachability semantics must poll getWatchStatus() and stop the session manually on persistent disconnect.

Logging

The SDK does not write to platform log streams beyond assert-guarded print calls. Hosts that need structured logging should subscribe to the Stream<SessionEvent> and log at the call site.