ThrustLab

Error handling

Every API error becomes a typed Python exception. The exception class tells you the category (so you can branch on retry vs surface vs fix-input); the .code attribute tells you the specific failure (so you can display a useful message).

Exception hierarchy

Every SDK exception inherits from ThrustlabError. Only the HTTP-derived errors inherit from APIError — the other four are siblings of APIError, not descendants, so except APIError does not catch them:

ThrustlabError                 # base — catch this for "any SDK error at all"
├── APIError                   # HTTP error response — carries code/type/request_id
│   ├── AuthenticationError    # 401 invalid_api_key
│   ├── ForbiddenError         # 403 insufficient permission
│   ├── NotFoundError          # 404
│   ├── ValidationError        # 400 / 422 with field-level error
│   ├── ConflictError          # 409 conflict (e.g. idempotency-key replay)
│   ├── RateLimitError         # 429 after retries exhausted
│   └── APIServerError         # 5xx after retries exhausted
├── ConfigurationError         # SDK misconfigured (no api_key, etc.)
├── NetworkError               # transport-level failure (DNS, connect, etc.)
├── SignatureVerificationError # webhook signature mismatch
└── AmbiguousComponentError    # .one() / .find() matched more than one component

Because ConfigurationError, NetworkError, SignatureVerificationError, and AmbiguousComponentError are not APIError subclasses, a bare except APIError around a call that constructs a Client() or calls .one() will not catch a bad-config or ambiguous-lookup failure — catch ThrustlabError if you want one handler for everything.

Import from thrustlab.exceptions:

from thrustlab.exceptions import (
    ThrustlabError, APIError, APIServerError, AuthenticationError,
    ForbiddenError, NotFoundError, ConflictError, ValidationError,
    RateLimitError, NetworkError, SignatureVerificationError,
    ConfigurationError, AmbiguousComponentError,
)

Common attributes

Every API exception carries:

AttributeNotes
.codeStable machine-readable code (invalid_request, card_declined, etc.)
.typeError envelope type field (validation_error, rate_limit_error, etc.)
.messageHuman-readable; safe to show to end users
.request_idServer-side correlation id; include this in support tickets
.http_statusHTTP status code that raised
.paramValidationError only — the offending body / query field
.retry_afterRateLimitError only — seconds, may be None

Branching by category

from thrustlab.exceptions import (
    AuthenticationError, ValidationError, RateLimitError,
    NotFoundError, APIServerError,
)

try:
    sim = client.simulations.create(project_id="proj_xxx", ...)
except ValidationError as exc:
    print(f"{exc.code}: {exc.message} (param={exc.param})")
except RateLimitError as exc:
    print(f"slow down — retry after {exc.retry_after}s")
except NotFoundError:
    print("project doesn't exist")
except AuthenticationError:
    print("revoked or invalid API key")
except APIServerError as exc:
    print(f"server error {exc.http_status}, request_id={exc.request_id}")

Branching by code

When two different errors share a category but need different handling, use the code attribute. Codes are stable — adding a new code is non-breaking, changing one is breaking.

from thrustlab.exceptions import APIError, NotFoundError, ValidationError

try:
    client.simulations.create(...)
except ValidationError as exc:
    if exc.code == "invalid_component_id":
        prompt_user_to_pick_component(exc.param)
    else:
        raise
except NotFoundError as exc:
    if exc.code == "component_not_found":
        prompt_user_to_pick_component(exc.param)
    else:
        raise
except APIError as exc:
    # 402 insufficient compute units is NOT in the SDK's status->class map, so it
    # surfaces as a bare APIError rather than a dedicated subclass.
    if exc.http_status == 402 and exc.code == "credits_insufficient":
        prompt_user_to_top_up()
    else:
        raise

insufficient_credits_error (HTTP 402, code credits_insufficient) has no dedicated exception class — 402 isn't in the SDK's status-to-class map, so it always raises the base APIError. Branch on .http_status == 402 (or .code), not on an exception type.

The full code catalog by resource is in the errors guide.

Catch-all + report

from thrustlab.exceptions import APIError

try:
    client.simulations.create(...)
except APIError as exc:
    log.warning(
        "thrustlab call failed",
        extra={
            "code": exc.code,
            "type": exc.type,
            "request_id": exc.request_id,
            "http_status": exc.http_status,
        },
    )
    raise

request_id is the most important field to log — it lets ThrustLab support trace a specific failed request server-side.

Retry exhaustion vs. fail-fast

FailureRetried automatically?Exception class on raise
429 with Retry-Afteryes (up to max_retries)RateLimitError after exhaustion
5xxyes (up to max_retries)APIServerError after exhaustion
Network erroryes (up to max_retries)NetworkError after exhaustion
400 / 422 validationnoValidationError immediately
401 / 403 / 404 / 409nomatching class immediately

Validation errors fail-fast because retrying won't help — the server's answer won't change for the same body. See retries & timeouts for the retry policy.

Recipe: surface validation errors at the right field

from thrustlab.exceptions import ValidationError

try:
    client.projects.create(name="")  # too short
except ValidationError as exc:
    if exc.param == "name":
        print(f"name field error: {exc.message}")
    else:
        raise

Recipe: handling authentication failures

All API-key failures — missing, malformed, revoked, or belonging to a deactivated user — collapse to a single AuthenticationError with code invalid_api_key. This is deliberate anti-enumeration: the server never tells a caller which of those applies, so the SDK can't either. Don't branch on a revoked-vs-invalid distinction; treat any AuthenticationError the same way (prompt for a fresh key):

from thrustlab.exceptions import AuthenticationError

try:
    client.users.me()
except AuthenticationError:
    prompt_for_new_key()

See also

We use cookies and analytics tools to understand how you use ThrustLab and improve the experience. Privacy Policy

Python SDK — Error handling · ThrustLab API