Status codes
Every xcaicx_status value, what causes it, and what to do. This is the page to link from your firmware's diagnostics screen.
xcaicx_status_str() gives the name; xcaicx_last_error() gives a thread-local detail string written to be read by a field technician. Surface both.
General#
| Code | Value | Cause | Fix |
|---|---|---|---|
XCAICX_OK | 0 | Success | — |
XCAICX_ERR_INVALID_ARG | -1 | NULL pointer, or a value outside its allowed range | Check the call against the C reference |
XCAICX_ERR_OOM | -2 | Allocation failed | Reduce max_queue_depth, resolution, or stream count |
XCAICX_ERR_NOT_INITIALIZED | -3 | Handle used before creation or after destruction | Check lifetime; a use-after-destroy often shows up here |
XCAICX_ERR_INTERNAL | -4 | A bug on our side | Capture xcaicx_last_error() and the log at DEBUG, then send it to support |
XCAICX_ERR_UNSUPPORTED | -5 | The operation is not available in this build | Check your CMake options — usually a backend not compiled in |
XCAICX_ERR_ABI_MISMATCH | -6 | struct_size was not set, or names a struct this library does not know | Call the matching *_init before setting fields |
Careful
ABI_MISMATCH almost always means a missing xcaicx_config_init() or xcaicx_stream_config_init(). A zeroed struct has struct_size == 0, which the library cannot interpret. It is also what you get from a struct memcpy'd out of a different SDK version.
Licensing#
These are the codes firmware must handle individually. Collapsing them into "license bad" means a technician has to guess, and it is the single most common integration mistake.
| Code | Value | Cause | What the technician should do |
|---|---|---|---|
XCAICX_ERR_LICENSE_MISSING | -100 | No token at license_path, and none in state_dir | Run provisioning |
XCAICX_ERR_LICENSE_MALFORMED | -101 | Not three dot-separated parts with an XCAICX1 header | Re-copy the token; check for truncation or added whitespace |
XCAICX_ERR_LICENSE_BAD_SIGNATURE | -102 | Ed25519 verification failed over the encoded payload | See the box below — this one has a non-obvious cause |
XCAICX_ERR_LICENSE_EXPIRED | -103 | Past exp and past grace_days | Renew and install a new token |
XCAICX_ERR_LICENSE_NOT_YET_VALID | -104 | Current time is before nbf | Check the clock; a camera booting at epoch trips this |
XCAICX_ERR_LICENSE_WRONG_DEVICE | -105 | Token's dev does not match this camera's fingerprint | SD card swapped between units? Board replaced? |
XCAICX_ERR_LICENSE_REVOKED | -106 | Licence id is on the revocation list | Commercial issue — contact the vendor |
XCAICX_ERR_LICENSE_CLOCK_ROLLBACK | -107 | Clock is behind the persisted high-water mark | Fix NTP or replace the RTC battery |
XCAICX_ERR_ENTITLEMENT_DENIED | -108 | A module in cfg.modules is not in the SKU | Upgrade the licence, or drop the module from the config |
XCAICX_ERR_LIMIT_EXCEEDED | -109 | Stream cap, FPS cap or metered quota reached | Close a stream, or upgrade |
XCAICX_ERR_ACTIVATION_REQUIRED | -110 | Holding a batch key that has never been exchanged | Connect to the network once, or pre-provision offline |
Do not
BAD_SIGNATURE on every camera at once, right after a server deployment, almost never means a corrupted token. It means the licensing server's signing key and the public key compiled into the SDK no longer match. Check GET /healthz on the authority and compare public_key against the key in your SDK build. The camera-side symptom points nowhere near the cause, which is what makes this one expensive.
Distinguishing the licence states#
XCAICX_LIC_GRACE is not an error. It means expired but inside the grace window, and everything still runs. Read it from xcaicx_engine_license_info() and surface it as a warning so the customer renews before the hard stop.
if (info.state == XCAICX_LIC_GRACE)
warn("Licence expired. %lld days of grace remaining.",
(long long)(info.seconds_remaining / 86400));Runtime#
| Code | Value | Cause | Fix |
|---|---|---|---|
XCAICX_ERR_MODEL_LOAD | -200 | Model bundle missing, corrupt, or wrong for this backend | Check model_dir; treat as fatal in production firmware |
XCAICX_ERR_BACKEND | -201 | The backend failed to initialise or to run | Check the accelerator driver; check notes() in the startup log |
XCAICX_ERR_FRAME_FORMAT | -202 | Unsupported format, bad geometry, or data_len too small | xcaicx_last_error() names both byte counts |
XCAICX_ERR_QUEUE_FULL | -203 | Worker is behind — backpressure, not an error | Drop the frame and count it |
XCAICX_ERR_NO_RESULT | -204 | poll() found nothing ready | Normal; poll again later |
Note
QUEUE_FULL and NO_RESULT are flow-control signals, not failures. Logging them at ERROR will bury the real errors in a busy camera's log. Count them as metrics instead — a rising QUEUE_FULL rate is the earliest warning that a site has outgrown its board.
Mapping to Python#
| C status | Python exception |
|---|---|
-100 … -107, -110 | xcaicx.LicenseError |
-108 | xcaicx.EntitlementError |
-109, -203 | xcaicx.LimitError |
| everything else | xcaicx.XcaicxError |
.status carries the numeric value, so this table works from Python too:
except xcaicx.XcaicxError as exc:
if exc.status == -102:
alert("signing key mismatch — check /healthz on the authority")HTTP statuses from the authority#
Not xcaicx_status values, but they surface in the same investigations.
| HTTP | Endpoint | Meaning |
|---|---|---|
400 | /v1/activate | device_id missing or too long |
401 | /v1/usage | Missing or unverifiable bearer token |
401 | /v1/admin/* | Bad admin key |
402 | /v1/activate | Batch exhausted — all purchased units activated |
403 | /v1/activate | Bad signature, expired, revoked, or a signed key this authority never issued |
403 | /v1/usage | Token bound to a different device, or licence revoked |
404 | admin | Unknown customer, batch or licence |
429 | /v1/activate | Edge rate limit — 20 requests per 10s per IP |
A 429 on a factory provisioning line means the line is faster than the limit. Allowlist its egress IP rather than raising the global limit.
Next#
- Troubleshooting — symptom-first, for when you do not have a code
- C ABI reference
- OEM integration