Troubleshooting
Status codes that are part of normal flow
| Status | Meaning | What to do |
|---|---|---|
QAR_STATUS_ONBOARDING_SESSION_NOT_FOUND | The onboarding ID has no persisted slot on this machine | Fall back to qar_runtime_onboard — this is the expected first-run path |
QAR_STATUS_PAKE_ERROR | Pairing code wrong or expired (codes live ~10 s) | Re-prompt the user for the current code and retry |
QAR_STATUS_ONBOARDING_HUB_UNREACHABLE | Discovery couldn't reach a Hub | Verify the Hub is running, host/port (remote Hubs need a QarOnboardHostExt), and firewall/multicast on the network |
QAR_STATUS_ONBOARDING_SESSION_STILL_ACTIVE | forget called while the session is active | Call qar_session_handle_destroy first, then forget |
QAR_STATUS_MEDIA_STREAMING_STREAM_IS_PAUSED | The receiver paused the stream, for example because its display is not visible | Keep the sender and skip the frame; it resumes when the receiver does. Recreating the sender only restarts the negotiation |
QAR_STATUS_MEDIA_STREAMING_STREAM_IS_RECONNECTING | The stream lost its peer and is reconnecting | Skip the frame and keep calling; a dead peer is noticed within ~2 s, a restarted one streams again within a few seconds |
QAR_STATUS_RENDERING_PRODUCER_APP_VOLUME_UNRESOLVED | The sender's app volume does not resolve yet (or was closed), so there is no app content space to hand poses out in | Keep calling begin_frame; create the volume before the sender and recreate one you closed |
QAR_STATUS_RENDERING_PRODUCER_STREAM_IS_CLOSED | The sender's stream was closed for good | Destroy the sender; create a new one if the target is still in the peer list |
Checking results
Every fallible call reports success or failure the same way — inspect it rather than assuming success. The status code is what you branch on to tell a normal-flow condition (above) apart from a real error:
QarRejoinInit rejoin = qar_rejoin_init_default();
rejoin.onboarding_id = onboarding_id;
QarResult r = qar_runtime_rejoin(runtime, &rejoin, NULL, NULL, NULL, &session);
if (qar_result_is_success(r))
{
/* use session */
}
else if (qar_result_has_code(r, QAR_STATUS_ONBOARDING_SESSION_NOT_FOUND))
{
/* expected first-run: fall back to qar_runtime_onboard */
}
else
{
qar_result_log_if_error(r); /* log status code + message */
}
The C# snippet above is illustrative — it tracks the Quaternar.Qaros binding surface but
is not yet compiled from a published sample project.
Frequent integration mistakes
- Forgetting to persist the onboarding ID. The ID returned by onboarding is the only key to the persisted identity. Losing it means pairing again.
- Calling API functions from inside callbacks. Result/progress/update callbacks run on library threads; blocking API calls from there can deadlock. Queue to your own thread.
- Zero-initializing init structs by hand. Always use
qar_*_default()— it stampsheader.type, without which calls fail. - Mismatched projection metadata. If streamed content "swims" or sits at the wrong depth, your
show_framenear/far or per-view poses don't match what you actually rendered with. This is a metadata bug, not a network problem — see Rendering Streams. - Confusing panel and volume pose conventions. Panel pose = midpoint of the top edge; volume pose = cuboid center.
- Destroying the runtime while async operations run. Cancel (and wait for the exactly-once result callback) before teardown.
Where to look
- Library logs — enable with
QarLibraryInit.enable_console_loggingand/orlog_folder_path. - Hub-side logs — the QAROS Hub log folder on the Hub machine.
- Warping monitor — the visualizer's timing view shows per-volume stream latency/FPS/jitter, which quickly separates "my app renders slowly" from "the network is dropping frames".
Crash dumps
If your process crashes while the C API is loaded, it writes <time>_<process>_<pid>.crash.txt and a .dmp into crashes/ under the log_folder_path you passed to qar_library_init (otherwise %TEMP%\quaternar\crashes). Hub processes write theirs next to their logs (see Running the Hub). Attach both files and the logs beside them to a bug report.
For a GPU crash or device loss, set QAR_VULKAN_CRASH_DIAGNOSTIC=1 before starting the processes. With the Vulkan SDK's crash diagnostic layer installed, QAROS loads it and it names the command the GPU was executing (output folder: the layer's CDL_OUTPUT_PATH); without the layer the variable does nothing. It is off by default because it records every command buffer.
Getting help
- GitHub Issues on this repository for bugs and feature requests.
- GitHub Discussions for integration questions.