Skip to content
OAOpenAppPhysical Security as a Service
Login

Errors

The SDK raises typed exceptions for every failure mode. All exceptions live in openapp_sdk.errors and inherit from SdkError.

SdkError
├── ApiError # backend returned a structured error envelope
├── HttpError # backend returned a non-OK response w/o a JSON envelope
├── AuthError # 401/403, invalid token format, missing credentials
├── TransportError # network failure, timeout, DNS, TLS
├── ValidationError # backend 400/422 with field-level errors
├── ConfigError # client misconfiguration (bad base URL, unusable key)
└── SerializationError # response body could not be decoded

Both sync and async clients raise the same exception types.

from openapp_sdk.errors import (
ApiError,
AuthError,
TransportError,
ValidationError,
)
try:
client.orgs.create(name="")
except ValidationError as err:
# err.fields is a dict[str, list[str]] of field → messages.
for field, messages in err.fields.items():
print(f"{field}: {', '.join(messages)}")
except AuthError:
print("API key expired — rotate it.")
except ApiError as err:
print(f"[{err.status}] {err.code}: {err.message}")
except TransportError:
print("Network issue — will retry.")
FieldAvailable onDescription
statusApiError, HttpError, AuthError, ValidationErrorHTTP status code.
codeApiError, ValidationErrorBackend-defined error code (e.g. ORG_NAME_REQUIRED).
messageAllHuman-readable message.
request_idAll that wrap an HTTP responseBackend request ID, forwarded from the X-Request-Id response header. Quote this when reporting bugs.
fieldsValidationErrorField-level validation messages.
causeAllThe underlying exception (e.g. httpx.ReadTimeout), if any.

The SDK retries transient failures (HTTP 5xx, connection errors, timeouts) automatically. Errors you see are final — you don’t need your own retry loop for the common cases. Tune how many attempts the core transport may take with Client.connect(..., max_retries=...); see the Python SDK overview and Errors & retries.

Every response carries an X-Request-Id header; the SDK attaches it to the raised exception and emits it in log records. When you file a support ticket, include the request ID — backend logs are keyed on it.

try:
client.devices.get("dev_missing")
except ApiError as err:
log.error("Device lookup failed", extra={"openapp_request_id": err.request_id})
raise
  • Catching Exception: don’t. Always catch SdkError or a specific subclass so the runtime halts on genuinely unexpected errors.
  • Comparing err.code strings: the backend defines the set of codes; treat unknown codes as soft failures rather than asserting a closed set.
  • Mutating err.fields: it’s a snapshot of the server’s response; treat it as read-only.