Errors and recovery

Recovery must preserve device identity and avoid turning retries into duplicate commands, corrupt certificates, or partially installed firmware.

Provisioning errors

All bindings map RFC 9457 responses to the same taxonomy:

Error code

HTTP

Retry

Action

DDI-1-400-MALFORMED-BODY

400

no

fix request serialization

DDI-1-401-NO-CREDENTIAL

401

no

restore the correct bootstrap credential or client certificate

DDI-1-403-NOT-RECOGNIZED

403

no

wait for registrar binding or correct model/serial

DDI-1-403-IDENTITY-BLACKLISTED

403

never

quarantine the device and escalate; this is terminal

DDI-1-422-MALFORMED-CSR

422

no

regenerate a valid signed CSR

DDI-1-429-RATE-LIMITED

429

bounded

honor Retry-After, then back off with jitter

DDI-1-503-BACKEND-UNAVAILABLE

503

bounded

retry with exponential backoff and jitter

The SDK provisioning clients perform bounded retries for 429 and 503. A surfaced retryable error means their retry budget is exhausted; persist the attempt state and apply a slower outer schedule rather than starting a tight loop.

Python raises typed subclasses of ProvisioningError. Android raises typed ProvisioningException subclasses. C returns fgai_prov_error_t and provides last-error accessors. Branch on those types or codes, not localized error text.

Certificate rotation

Generate a new key for rotation. Write the new key and certificate to temporary storage, verify that they match and chain to the trusted CA, then atomically switch the active identity. Keep the prior pair until the new pair completes an mTLS proof. If proof fails, restore the prior pair and retry according to the certificate’s remaining validity.

Do not expect rotation to return a new CA chain, MQTT endpoint, or expiry field. The implemented response contains only thing_id and certificate.

MQTT connection loss

Use a stable fgai-<thing_id> client ID, persistent session, one-hour default session expiry, and exponential reconnect with jitter.

After each reconnect:

  1. re-subscribe commands and SHARED pushes even if the broker reports a resumed session;

  2. restore all message callbacks before application traffic;

  3. republish the complete CLIENT attribute snapshot;

  4. resume telemetry with a monotonic application sequence; and

  5. deduplicate commands by command_id before executing side effects.

Python performs reconnect and re-subscription automatically. C reconnects internally and exposes an informational disconnect callback. The current Android client does not reconnect automatically; the host application must implement the sequence above.

Inbound message failures

Reject malformed protobuf messages without applying them. A shared-attribute callback must apply the value durably before acknowledging it. Python currently acknowledges after the callback returns even if the callback raises, so callbacks should contain their own error handling and must not signal success by throwing. Android and C adapters should follow the same explicit effect-then-ack rule.

Command handlers should be short and idempotent. Persist the command ID and operation state before an irreversible side effect. If work is long-running, hand it to a durable local queue and respond according to the command contract; do not block an MQTT network callback.

OTA failures

Failure

Binding signal

Recovery

HTTP or transport

Python OtaHttpError/OtaError; Android ApiResponse; C transport/HTTP code

retry safe GET/HEAD with bounded backoff; send feedback when the action supplies a feedback link

unsafe HAL link

argument/value failure; C FGAI_OTA_ERR_UNSAFE_LINK

reject permanently and report a platform contract error

SHA-256 mismatch

Python OtaIntegrityError; Android OtaIntegrityException; C FGAI_OTA_ERR_INTEGRITY

delete the temporary file, report failure, and do not install

parse failure

document/JSON exception; C FGAI_OTA_ERR_PARSE

retain current firmware and report malformed assignment

local I/O

exception; C FGAI_OTA_ERR_IO

preserve the running slot, reclaim storage, then retry if safe

Downloads must land in a temporary file or inactive slot. Verify expected size when supplied and SHA-256 before an atomic rename or boot-slot switch. On boot failure, the product bootloader—not DDI—must roll back to the known-good slot.

Licensing failures

The device licensing HTTP routes are not published. Treat connection failure or 404 from those compatibility methods as “capability unavailable,” not as a license grant. The Android offline parser is not signature verification and must not be used to fail open. See Device licensing compatibility.

Observability without secret leakage

Log operation name, non-secret thing_id, error code, HTTP status, retry count, and correlation ID. Never log bootstrap or enrollment tokens, JWTs, private keys, PEM bodies, raw authorization headers, HawkBit upstream credentials, or command payloads that may contain secrets.