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.
The wear SDK exposes a small typed error hierarchy. Most adapters emit one of three error subtypes; vendor-specific failures bubble through originalException.
Base type
class SynheartWearError implements Exception {
final String message;
final String? code;
final Exception? originalException;
}
Subtypes:
| Class | Code | Trigger |
|---|
PermissionDeniedError | PERMISSION_DENIED | OS or adapter permission denied. |
DeviceUnavailableError | DEVICE_UNAVAILABLE | No wearable / source available. |
NetworkError | NETWORK_ERROR | Cloud adapter HTTP/RPC failure. |
originalException carries the platform-specific underlying error so callers can inspect when the typed code is too coarse.
Permission denied
Triggered by:
- HealthKit refusing access to a requested
HKObjectType.
- Health Connect missing a permission grant for
HeartRateRecord (etc.).
- BLE permission revoked at OS level.
- Vendor OAuth flow cancelled by user or rejected by vendor.
requestPermissions returning denied for any requested type.
Recovery:
- For OS permissions: surface a UI rationale and call
requestPermissions again with the missing types.
- For OAuth: route the user back through the vendor’s auth URL.
- For revoked permissions: the SDK reports
ConsentStatus.revoked, distinct from denied (never granted vs previously granted).
Device unavailable
Triggered when the adapter cannot find a viable data source:
- HealthKit not present (iPad without paired iPhone).
- Health Connect not installed or unsupported on the Android version.
- BLE HRM scan times out without finding a 0x180D device.
- Vendor webhook subscription not active.
streamHR invoked while no underlying source is connected.
Recovery: the SDK retries on streamHR/streamHRV reconnects, but readMetrics() calls fail fast. Hosts should display a “no device connected” UI and let the user reconnect.
Network error
Triggered by HTTP/RPC failures in the cloud adapters (WHOOP, Garmin, Fitbit). The originalException wraps the underlying transport error.
Common causes:
- Vendor token expired (caller should refresh OAuth).
- Vendor rate-limit (HTTP 429); adapters back off.
- Server-side outage (5xx).
- TLS validation failure.
Recovery: per-adapter, with exponential backoff. Token-related failures may surface as PermissionDeniedError instead, depending on the vendor.
Adapter-specific errors
HealthKit
| Code | Cause |
|---|
HKErrorAuthorizationDenied | User denied at HealthKit prompt. |
HKErrorAuthorizationNotDetermined | Not asked yet. |
HKErrorNoData | Sample query returned empty. |
HKErrorRequiredAuthorizationDenied | Required type missing in HKHealthStore request. |
The SDK maps these to PermissionDeniedError or DeviceUnavailableError as appropriate.
Health Connect
| Code | Cause |
|---|
INSTALL_REQUIRED | Health Connect not installed on the device. |
MIGRATION_REQUIRED | User must migrate from older Google Fit. |
NO_PERMISSIONS | Permission grant missing. |
The SDK surfaces these as DeviceUnavailableError (install/migration) or PermissionDeniedError (permissions).
BLE HRM
| Failure | Surface |
|---|
| Bluetooth permission denied | PermissionDeniedError("Bluetooth"). |
| Adapter off | DeviceUnavailableError("Bluetooth disabled"). |
| Scan timeout (no 0x180D) | DeviceUnavailableError("No HRM found"). |
| Connection lost mid-stream | streamHR emits a NetworkError-shaped event, then attempts reconnect. |
| Device sends malformed packet | Sample dropped, stream continues. |
WHOOP (cloud)
| Failure | Surface |
|---|
| OAuth token expired | PermissionDeniedError; caller refreshes via WhoopProvider. |
| Webhook subscription invalid | DeviceUnavailableError; caller re-subscribes. |
| RAMEN delivery channel closed | Caller-transparent reconnect; final close emits no error. |
fetchRawDataForFlux returns 4xx | NetworkError with the vendor’s response in originalException. |
Garmin (cloud)
| Failure | Surface |
|---|
| OAuth token expired | PermissionDeniedError. |
| Webhook arrived but REST pull (DeliveryHint.ping) fails | NetworkError; no WearMetrics emitted for this event. |
| Rate-limited (429) | Adapter backs off; caller sees no error unless persistent. |
Garmin Health SDK (RTS)
| Failure | Surface |
|---|
| License missing | DeviceUnavailableError("Garmin Health SDK not licensed") at initialize. |
| Pairing failed | DeviceUnavailableError. |
| RTS connection drop | Auto-reconnect; logged. |
Apple Health XML backfill
| Failure | Surface |
|---|
export.zip malformed | SynheartWearError("Invalid Apple Health export"). |
| XML parse error | Same; the underlying XmlParserException is in originalException. |
| Runtime backfill ingest fails | Bubbles up the runtime’s negative return code. |
Logging policy
The SDK logs at info/warn and never logs:
- Vendor OAuth tokens.
- Personal
device_ids in plain (debug builds may; production ones do not).
- Raw HRV/RR sample bytes.
- XML payloads beyond malformed-record line numbers.
WearMetrics snapshots are not logged. Hosts that need observability should collect them at the consumer layer.
Validation invariants
Regardless of error state:
WearMetrics.timestamp is always UTC ISO-8601.
- Numeric metric values are finite (NaN/inf get filtered to
null).
- Event ordering is preserved within a single adapter; cross-adapter ordering uses
timestamp.