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.
Typical HTTP outcomes
Section titled “Typical HTTP outcomes”| Situation | Typical status | Notes |
|---|---|---|
| Missing or invalid credentials | 401 | Rotate or fix the API key; see Authentication. |
| Allowed auth but insufficient role / wrong org | 403 | Often org or route scope; see Organization context & pagination. |
| Unknown resource | 404 | Id or org filter does not resolve for the caller. |
| Validation / bad payload | 400, 422 | Prefer structured details when the backend emits them; Python maps some cases to ValidationError. |
| Rate limiting | 429 | Eligible for automatic retries in core-backed clients when the server signals retryability (and honoring Retry-After when present). |
| Upstream or service errors | 5xx | Eligible for automatic retries in core-backed clients. |
Handling failures
Section titled “Handling failures”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.
The Node client throws AuthError for auth-related failures (including 401 / 403) and ApiError for other mapped failures. ApiError may include an optional code when the backend JSON provides one.
import { AsyncClient, ApiError, AuthError } from "@tomers/openapp-sdk";
const client = new AsyncClient("https://api.openapp.house/api/v1_openapp_YOUR_SECRET");
try { await client.listOrgs();} catch (e) { if (e instanceof AuthError) { console.error("auth:", e.message); } else if (e instanceof ApiError) { console.error("api:", e.message, e.code); } else { throw e; }}High-level methods return Result<_, SdkError>. Use status() for HTTP-backed errors and is_retryable() if you implement your own backoff on top of the built-in transport retries.
use openapp_sdk::{Client, SdkError};
let client = Client::builder() .api_key("https://api.openapp.house/api/v1_openapp_YOUR_SECRET") .build()?;
match client.orgs().list().await { Ok(orgs) => { /* … */ } Err(SdkError::Api { status, body }) => { eprintln!("{status}: {}", body.message); } Err(e) if e.is_retryable() => { eprintln!("transient: {e}"); } Err(e) => return Err(e.into()),}With NewAPIClient, HTTP is executed by openapp-sdk-core (same transport stack as other bindings). Operation helpers return Go error on failure; the HTTP response (when present) carries status and body for inspection.
import ( "context" openapiclient "github.com/tomers/openapp-sdk/go")
resp, httpResp, err := client.OrgsAPI.ListOrgs(ctx). OutputOptions(*openapiclient.NewMultiResourceOutputOptionsQuery(false, false, false)). Pagination(openapiclient.PaginationQuery{Limit: openapiclient.PtrInt32(50), Offset: openapiclient.PtrInt32(0)}). Execute()if err != nil { return err}defer httpResp.Body.Close()// use resp; optional: httpResp.StatusCode for loggingFor error types generated alongside the client, see the sdk-go API docs for your release.
Retries
Section titled “Retries”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.
| Surface | How to tune |
|---|---|
| Rust | Client::builder().retry_policy(RetryPolicy { … }) — see RetryPolicy in openapp_sdk::retry (max_retries, backoff bounds, total_deadline). Use RetryPolicy::none() for tests. |
| Python | Client.connect(..., max_retries=n) — forwards to the core policy’s max_retries (other backoff fields stay at core defaults). |
| TypeScript | Retries 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. |
| Go | Same 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.
Related
Section titled “Related”- Authentication — credentials and client construction.
- Organization context & pagination — 403 / 404 in multi-org setups.
- Python — Errors — full Python exception reference.
- API reference — operations and schema names for error bodies.