Diagnostics & errors
Device status, automatic connection recovery and error handling.
Diagnostics & errors
Read first: Debug logs (turn on logs before reproducing the problem) · What you get: read device status, automatic connection recovery and error types
To locate a symptom, see Common questions; this chapter covers the tools for reading device status and handling errors.
Status and diagnostics
device.status() returns ReadChannel<GloveStatus>. Common fields include connected, stale,
timestamp, last_error, reconnect_count, and fields. Subscribe to status when continuous
monitoring is needed; use latest() when only the current value matters.
For the complete fields, see GloveStatus.
Automatic connection recovery
Automatic recovery applies only to a source that was successfully started and then disconnected
unexpectedly; a first-run connection failure is returned synchronously by start(). During
recovery, only the affected source becomes connected=false, stale=true and reports the reason
through last_error, while other sources keep running. After identity confirmation, recovery
makes it connected=true again, clears last_error, and increments reconnect_count. If it was
already start()ed before the recovery and data is received again, stale=false; a source that
has not been start()ed keeps stale=true after reconnecting until it starts receiving current
data.
Sending haptics or other device commands while disconnected fails immediately with a
not-connected error; they are not queued and not replayed. The current haptic setting is cleared;
after recovery it must be set again by the application. Explicitly calling stop() or close()
cancels recovery. For the same identity-confirmed device, retargeting output that is already
ready and still valid can continue to be used after recovery and is not recalibrated merely
because of the reconnection.
sdk.health() returns the aggregate status of the entire SDK, and glove.health() returns the
status of all configured gloves. To judge whether the runtime is healthy, read and record the
running health before the stop request:
const auto running_health = glove.health();
std::cout << "running health ok=" << running_health.ok << '\n';Health includes:
ok: whether it is currently healthy.warning_countanderror_count: cumulative diagnostic counts.devices: the connection, staleness, and error state of each source.last_error: summary of the most recent error.runtime_overrun: whether any runtime task overrun has been observed.topic_callback_error: indicates callback errors recorded during SDK data processing. In C++, an exception thrown by the callback passed tosubscribe()does not escape the callback boundary and does not settopic_callback_error; to observe it, record or handle it inside the callback.
After actively stopping and waiting for the run loop to finish, you can read the
stopped lifecycle health again before closing to confirm the connected/stale lifecycle
state of the devices. After telemetry stops, devices enter stale=true, so even with
warning_count=0 and error_count=0, the aggregate value may still be ok=false. This is the
expected stopped lifecycle state; it does not indicate a runtime failure and does not change the
meaning of the shared Health.ok. For the complete sequence, see
foreground_runtime.cpp and
background_runtime.cpp.
For the complete returned fields, see Health.
Error handling
Catch the most specific exception type for the failure scenario, and prefer the structured information provided by the exception:
| Exception | Typical meaning |
|---|---|
InvalidArgumentError | The configuration, frame, haptics, or call parameters are invalid |
ClosedHandleError | The SDK or Glove is already closed |
StaleUpdateError | The update result has been consumed or is no longer valid |
LifecycleError | The lifecycle order or run mode does not allow the current operation |
UnavailableFeatureError | The current device, platform, or delivery does not support this capability |
CommandRejectedError | The control command was rejected |
GloveControlError | The device control command failed: not connected, device busy, timed out; the reason is in kind (GloveControlErrorKind) |
InternalError | The SDK cannot continue the operation |
PanicError | An unexpected runtime failure |
Device or target selection additionally has NotFoundSelectionError and
AmbiguousSelectionError, both carrying structured selector and candidate information.
Asynchronous wait cancellation uses the standard std::system_error(std::errc::operation_canceled).
Catch exceptions by specific recovery strategy, then use QnBotError as the final SDK error
boundary:
try {
auto device = glove.device("primary");
} catch (const qnbot::NotFoundSelectionError& error) {
for (const auto& candidate : error.candidates()) {
std::cerr << candidate << '\n';
}
} catch (const qnbot::QnBotError& error) {
std::cerr << error.what() << '\n';
}