Handle errors by category
| Error | Host response |
|---|---|
REQUEST_ABORTED |
End loading after cancellation; inspect recovery results as needed instead of showing a generic network failure |
AUTH_CHANGED |
Consent or account/token state changed; discard stale UI results, inspect the session and recreate clients |
AUTH_EXPIRED |
Platform session expired; sign in again or refresh through the platform flow |
CLOUD_BUSY, SHOP_BUSY |
Conflicting operation exists; wait for settlement and refresh instead of repeatedly submitting mutations |
NAVIGATION_ACTIVE |
End navigation before switching or removing the vehicle |
NETWORK_ERROR, REQUEST_TIMEOUT |
Outcome may be unknown; query and recover before deciding to retry |
RELEASE_PENDING |
Complete pending nearby revocation before starting another binding |
2901 |
Binding challenge or version conflict; query authorization again rather than blindly resubmitting the old challenge |
LEGACY_SCOPE_REQUIRED, LEGACY_SCOPE_CONFLICT |
Legacy origin is unclaimed or belongs elsewhere; verify it using upgrade guidance, never claim automatically |
HTTP uses HTTPS with a base including /api/v1; the SDK appends /app/... and does not follow redirects. Retain server error codes and HTTP status for diagnosis. Do not treat every failure as unavailable Bluetooth.
Cancellation and recovery
After cancellation, wait for the final callback before starting another mutually exclusive task. Cancellation is not a transaction rollback: binding, unbinding or payment may already have been confirmed remotely. Recover using authoritative server and terminal state.
Never automatically replay vehicle commands, restart payment or use a new purchase UUID to retry an unknown result. Centralize device workflow coordination so screens share the current account, target vehicle and operation lifecycle.
Release checks
| Area | Verify |
|---|---|
| Registration | Correct clientId, manufacturer scope, API environment and enabled services |
| Privacy and permissions | Related capabilities remain inactive before consent; denial, withdrawal and reauthorization work |
| Vehicles | Initial/repeated pairing, incorrect PIN, cancellation, switching, unbinding and account changes |
| Connectivity | Authentication still matches the target after Bluetooth or hotspot denial, disconnect and reconnect |
| Commerce | Manufacturer mismatch is rejected; address/price changes require a new quote; duplicate taps do not duplicate orders |
| Payment | Cold-launch and foreground URLs, cancellation and timeout recovery, server order queries; unconfirmed results never appear settled |
| Lifecycle | Background/foreground, process restart, token refresh and navigation restrictions |
| Binaries and signing | Correct platform package and version, host signing, system capabilities and third-party notices |
Verify these paths on real phones with supported terminals. Simulator runs, compilation, installation and successful cloud responses prove different stages; they do not replace actual vehicle-control and payment acceptance.
Diagnostic information
Report SDK and OS versions, action name, error code, time and reproducible steps. Include whether the account, vehicle or network changed. Redact logs and avoid collecting tokens, PINs, Wi-Fi passwords, device credentials, complete personal addresses or signed payment parameters.
Contact the platform for package delivery and registration issues. For instrument firmware and FreeRTOS, use the hardware SDK docs.
Route errors from callbacks
This example maps error codes to host UI decisions without replaying requests. Pass the callback to a cloud operation such as list. onValue receives the current operation’s result; onFailure ends loading and chooses a prompt, sign-in or query/recovery flow. The host still checks the operation generation.
import net.oou.mobile.ResultCallback
enum class FailureAction {
CANCELLED, RECREATE_SESSION, SIGN_IN, WAIT_FOR_ACTIVE,
QUERY_AND_RECOVER, INSPECT_BUSINESS_ERROR
}
fun cloudCallback(
onValue: (Any?) -> Unit,
onFailure: (FailureAction, String) -> Unit
): ResultCallback = object : ResultCallback {
override fun resolve(value: Any?) = onValue(value)
override fun reject(code: String, message: String?, cause: Throwable?) {
val action = when (code) {
"REQUEST_ABORTED" -> FailureAction.CANCELLED
"AUTH_CHANGED" -> FailureAction.RECREATE_SESSION
"AUTH_EXPIRED" -> FailureAction.SIGN_IN
"CLOUD_BUSY", "SHOP_BUSY" -> FailureAction.WAIT_FOR_ACTIVE
"NETWORK_ERROR", "REQUEST_TIMEOUT" -> FailureAction.QUERY_AND_RECOVER
else -> FailureAction.INSPECT_BUSINESS_ERROR
}
onFailure(action, code)
}
}
