Exceptions
Every error the SDK raises is a subclass of ConvilynError. Catch the base class for a single safety net, or catch a specific subclass when you want to branch on the failure mode.
Hierarchy
ConvilynError base — everything the SDK raises
├── AuthError no API key configured / malformed key
├── APIError HTTP 4xx / 5xx from the Convilyn API
│ ├── RateLimitError HTTP 429
│ ├── S3UploadError presigned upload step returned non-success
│ └── RetryExhaustedError retry policy ran out of attempts
├── JobFailedError turbo-lane job reached status `failed`
├── JobTimeoutError turbo-lane polling exceeded its timeout
├── GoalJobFailedError goal-lane workflow reached status `failed`
├── GoalJobTimeoutError goal-lane polling exceeded its timeout
└── WebSocketError goal-lane event stream could not proceed
ConvilynError
class ConvilynError(Exception): ...
Base class for everything the SDK raises. Catching ConvilynError is the recommended way to gate SDK calls behind a single error handler.
from convilyn import ConvilynError
try:
client.convert.create_and_wait(file=file, target_format="pdf")
except ConvilynError as e:
log.error("conversion failed", exc_info=e)
The SDK never raises a bare Exception — anything originating in the SDK is one of the classes below.
AuthError
Authentication or authorization failed before any HTTP call. Examples: no API key configured, malformed key prefix (cvl_... expected).
APIError
The Convilyn API returned a non-success HTTP response. Surfaces the envelope {code, message, details} as attributes.
class APIError(ConvilynError):
status_code: int
code: str
message: str
details: dict[str, Any]
RateLimitError
HTTP 429 — the SDK or caller exceeded the rate limit. Subclass of APIError. Catching APIError covers this case too.
S3UploadError
The presigned upload step of a file upload returned a non-success status. Subclasses APIError so callers catching APIError see it, while still being distinguishable for callers who want to retry uploads with their own policy.
RetryExhaustedError
The retry policy ran out of attempts before the request succeeded. Wraps the final APIError so callers see the last server-side status / code.
class RetryExhaustedError(APIError):
attempt_count: int # total attempts including this one
JobFailedError
A turbo-lane job (client.convert.wait, create_and_wait, …) finished with status failed. Attributes mirror the wire-side error envelope so you can correlate against backend logs.
class JobFailedError(ConvilynError):
job_id: str
processor_type: str
code: str
message: str
JobTimeoutError
A polling helper exceeded its timeout before the job reached a terminal status. The job is still alive on the backend — call client.convert.retrieve(job_id) to fetch its current state.
class JobTimeoutError(ConvilynError):
job_id: str
elapsed: float
timeout: float
GoalJobFailedError
Goal-lane (multi-step) job finished with status failed. Separate class from JobFailedError so you can distinguish turbo-lane conversion failures from goal-lane workflow failures in a try / except chain.
class GoalJobFailedError(ConvilynError):
job_spec_id: str
code: str
message: str
GoalJobTimeoutError
A goal-lane polling helper exceeded its timeout. Like JobTimeoutError but specific to goal-lane workflows.
WebSocketError
Raised when the goal-lane event stream cannot proceed. Covers four conditions:
- The SDK has no WebSocket URL configured (no
ws_urlctor arg, noCONVILYN_WS_URLenv var) - The transport's
connectstep raises (DNS, TLS, upgrade rejection) - A mid-stream message cannot be parsed into a
GoalEvent - The connection drops mid-stream
The original payload — when available — is attached on payload so callers can log it.
class WebSocketError(ConvilynError):
payload: str | None
Catching patterns
Catch everything from the SDK:
try:
...
except ConvilynError as e:
...
Branch on transient vs permanent:
try:
...
except RateLimitError:
# back off and retry later
except (RetryExhaustedError, JobTimeoutError):
# transient — caller should retry the workflow
except ConvilynError:
# something else SDK-originated
Distinguish lanes:
try:
...
except JobFailedError as e:
log.error(f"turbo-lane job {e.job_id} failed: {e.code}")
except GoalJobFailedError as e:
log.error(f"goal-lane workflow {e.job_spec_id} failed: {e.code}")