Skip to content
OAOpenAppPhysical Security as a Service
Login

Errors & retries

Non-success responses from the public API are HTTP errors with a body that is usually JSON shaped like ApiErrorResponse: a required message, optional code, optional details, and optional correlationId (see components/schemas in the API reference and packages/api-spec/openapi.json). Exact fields evolve with the bundle; treat unknown code values as forward-compatible.

Transport failures (timeouts, TLS, connection resets) surface before a successful HTTP status is returned. SDKs built on openapp-sdk-core share retry policy behavior for transient HTTP statuses and transport errors; see Retries below.

For Python-only exception hierarchy and attributes (request_id, ValidationError.fields, …), see Python — Errors.

SituationTypical statusNotes
Missing or invalid credentials401Rotate or fix the API key; see Authentication.
Allowed auth but insufficient role / wrong org403Often org or route scope; see Organization context & pagination.
Unknown resource404Id or org filter does not resolve for the caller.
Validation / bad payload400, 422Prefer structured details when the backend emits them; Python maps some cases to ValidationError.
Rate limiting429Eligible for automatic retries in core-backed clients when the server signals retryability (and honoring Retry-After when present).
Upstream or service errors5xxEligible for automatic retries in core-backed clients.

All raised failures inherit from SdkError. Catch specific subclasses (ApiError, AuthError, TransportError, …) instead of bare Exception.

from openapp_sdk.errors import ApiError, AuthError, SdkError, TransportError
try:
org = await client.orgs.get("01HORG00000000000000000000")
except AuthError:
...
except ApiError as err:
print(err.status, err.code, err.message)
except TransportError:
...
except SdkError:
...

Full hierarchy and fields: Python — Errors.

Python, Rust, TypeScript (AsyncClient via the C bridge), and Go (NewAPIClient + CGO) use the shared Rust transport: exponential backoff on 408, 425, 429, selected 5xx responses, and transport-level failures, with Retry-After honored when the server sends it. After retries are exhausted, you see a single final error — you typically do not wrap core-backed calls in another retry loop unless you have domain-specific requirements.

SurfaceHow to tune
RustClient::builder().retry_policy(RetryPolicy { … }) — see RetryPolicy in openapp_sdk::retry (max_retries, backoff bounds, total_deadline). Use RetryPolicy::none() for tests.
PythonClient.connect(..., max_retries=n) — forwards to the core policy’s max_retries (other backoff fields stay at core defaults).
TypeScriptRetries are configured inside the native bridge; expose tuning via package APIs only when the binding adds a knob — prefer upgrading the client if you need different limits.
GoSame core transport as NewAPIClient; no separate retry package in the generated Go layer.

Classification helper (Rust only): SdkError::is_retryable() mirrors the core’s notion of statuses safe to retry (including transport errors). Other languages surface post-retry failures through their normal error types.