Skip to main content

Error Handling

Every failed request raises a typed exception. All of them inherit from StronglyError, so you can catch broadly or narrowly. Import them from the top-level package:

from strongly import (
StronglyError, # base class for everything below
APIError, # an error response; the class is its HTTP status
ValidationError, # 400 - invalid request body or parameters
AuthenticationError, # 401 - missing, invalid, expired or revoked API key
PaymentRequiredError, # 402 - no credit or subscription for the request
PermissionDeniedError, # 403 - a missing scope, role or share
NotFoundError, # 404 - no such resource, or not visible to you
MethodNotAllowedError, # 405
RequestTimeoutError, # 408
ConflictError, # 409 - a duplicate or a busy resource
GoneError, # 410 - no longer available (an ended session)
PayloadTooLargeError, # 413
UnsupportedMediaTypeError, # 415
UnprocessableEntityError, # 422 - not possible in the resource's state
FailedDependencyError, # 424 - a provider refused the platform's credential
RateLimitError, # 429 - carries retry_after
InternalServerError, # 500
BadGatewayError, # 502
ServiceUnavailableError, # 503
GatewayTimeoutError, # 504
TimeoutError, # no response in time
ConnectionError, # the platform could not be reached
ResponseFormatError, # a success response the SDK cannot read
ConfigurationError, # no API key or no platform URL
)

A status with no class of its own raises APIError.

Catching errors​

from strongly import Strongly, NotFoundError, RateLimitError, APIError

client = Strongly()
try:
workflow = client.workflows.retrieve("wf-123")
except NotFoundError:
print("That workflow does not exist")
except RateLimitError as exc:
print("Rate limited; retry later:", exc)
except APIError as exc:
# Any other HTTP error (status >= 400)
print(f"API error {exc.status_code}: {exc}")

What an APIError carries​

APIError (and its subclasses) carry the response:

try:
client.apps.deploy("app-1")
except APIError as exc:
print(exc.status_code) # e.g. 409
print(exc.error_code) # the platform's code, e.g. "duplicate" or "not-found"
print(exc.message) # the platform's message
print(exc.details) # further detail, such as the invalid fields
print(exc.request_id) # X-Request-Id, to quote to support

An error page that is not the platform's (for example a proxy's HTML page) raises its status's class with the message HTTP <status> <reason>, and exc.body holds the page.

Retries​

The client retries a request that failed in a way worth repeating, with exponential backoff and full jitter, honoring a Retry-After header:

  • a GET, HEAD or PUT on a timeout, a dropped connection, 408, 429, 500, 502, 503 or 504;
  • a POST, PATCH or DELETE only on 429, or when the connection could not be opened: the platform may already have acted on any other failure, so repeating it could act twice.

Tune it with max_retries:

client = Strongly(max_retries=5)   # default is 3

Other errors raise immediately. A request without its own timeout uses the client's (60 seconds unless set).

Deletes raise, not return​

Every delete() returns None and raises on failure (it does not return a status object). Confirm a delete by catching the absence on a subsequent read:

client.memory.delete(memory_id)        # -> None
client.memory.retrieve(memory_id) # raises NotFoundError