Error Handling

pypresscart maps every HTTP error to a typed exception. You can catch the broad base class or the specific subclass.

Hierarchy

PresscartError                       # base class for anything this library raises
├── PresscartAPIError                # the API returned a non-2xx response
│   ├── BadRequestError       (400)
│   │   └── ValidationError   (400 with field-level `issues`)
│   ├── AuthenticationError   (401)
│   ├── PermissionError       (403)
│   ├── NotFoundError         (404)
│   ├── RateLimitError        (429)
│   └── ServerError           (5xx)
└── PresscartTransportError          # network failures (DNS, timeout, connection reset)

Import from the top level:

from pypresscart import (
    AuthenticationError,
    BadRequestError,
    NotFoundError,
    PermissionError,
    PresscartAPIError,
    PresscartError,
    PresscartTransportError,
    RateLimitError,
    ServerError,
    ValidationError,
)

What’s on every PresscartAPIError

Attribute

Type

Description

status_code

int

HTTP status (400, 401, …)

name

str | None

name field from the API body (e.g. "ForbiddenError")

message

str

Human-readable description

payload

dict

Full parsed JSON body (empty dict if the body wasn’t JSON)

Extras on specific subclasses:

  • ValidationError.issues: list[dict] — each item has path and message for the offending field.

  • RateLimitError.retry_after: float | None — parsed from the Retry-After response header.

PresscartTransportError wraps the underlying requests.exceptions.RequestException in __cause__.

Basic pattern

from pypresscart import NotFoundError, PresscartAPIError

try:
    campaign = client.campaigns.get("cmp_missing")
except NotFoundError:
    print("campaign does not exist")
except PresscartAPIError as exc:
    print(f"[{exc.status_code}] {exc.name}: {exc.message}")
    print("full payload:", exc.payload)

Handling validation errors

The API returns 400 with a structured issues array when the body fails schema validation:

from pypresscart import ValidationError

try:
    client.orders.create_checkout({"profile_id": "", "line_items": []})
except ValidationError as exc:
    for issue in exc.issues:
        print(f"field {issue['path']}: {issue['message']}")

Rate limiting

pypresscart automatically retries 429 responses up to max_retries times, honoring Retry-After. If you still exhaust retries, you get RateLimitError:

from pypresscart import RateLimitError

try:
    client.outlets.list()
except RateLimitError as exc:
    print(f"rate limited; retry after {exc.retry_after}s")

See Retry and Timeouts for tuning.

Permission vs authentication

  • AuthenticationError (401) — token missing, malformed, expired, or revoked. Re-mint the token.

  • PermissionError (403) — token is valid but the requested action isn’t in its scope (or crosses team boundaries). Grant the missing scope (see Authentication and Scopes).

from pypresscart import AuthenticationError, PermissionError

try:
    client.orders.create_checkout(body)
except AuthenticationError:
    refresh_or_alert()
except PermissionError:
    print("token lacks orders.create")

Network failures

from pypresscart import PresscartTransportError

try:
    client.auth.whoami()
except PresscartTransportError as exc:
    # The underlying requests exception is in __cause__
    import requests
    if isinstance(exc.__cause__, requests.Timeout):
        print("timeout — retry later")
    raise

PresscartTransportError is raised only after retries are exhausted.

Catch-all

If you just want “any pypresscart failure”:

from pypresscart import PresscartError

try:
    ...
except PresscartError as exc:
    log.exception("Presscart call failed")

HTTP status → exception cheat sheet

Status

Exception

Also retried?

400 (no issues)

BadRequestError

no

400 (with issues)

ValidationError

no

401

AuthenticationError

no

403

PermissionError

no

404

NotFoundError

no

429

RateLimitError

yes

500, 502, 503, 504

ServerError

yes

other 5xx

ServerError

no

non-HTTP failure

PresscartTransportError

yes (before raising)

Logging tips

Always log status_code, name, message, and payload for debuggability:

import logging
log = logging.getLogger(__name__)

try:
    client.campaigns.create(body)
except PresscartAPIError as exc:
    log.error(
        "presscart error %d %s: %s payload=%r",
        exc.status_code, exc.name, exc.message, exc.payload,
    )
    raise