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,HEADorPUTon a timeout, a dropped connection,408,429,500,502,503or504; - a
POST,PATCHorDELETEonly on429, 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