Error handling
Every error raised by the SDK derives from
VerdifaxError. Catch the base class for a single safety
net, or catch specific subclasses to branch on failure mode.
Hierarchy
VerdifaxError
├── ValidationError client-side input validation failed
├── ConnectionError transport-level failure (DNS / TCP / TLS / timeout)
└── APIError non-2xx HTTP response
└── StageError a specific pipeline stage rejected the run
Import
from verdifax import (
VerdifaxError,
ValidationError,
ConnectionError,
APIError,
StageError,
)
Note that verdifax.ConnectionError is not the builtin ConnectionError from OSError — it intentionally doesn't inherit from OSError so a generic except OSError doesn't silently swallow Verdifax errors.
When each is raised
| Exception | When | Action |
|---|---|---|
ValidationError | bad hex, empty route, wrong type, malformed payload | Fix the input — never retry |
ConnectionError | DNS failure, connection refused, timeout | Retry with backoff |
StageError | The orchestrator's pipeline rejected the run; check .stage | Investigate stage; usually a config or auth issue |
APIError | 5xx, 4xx other than the above | Retry on 5xx; investigate on 4xx |
Branching example
from verdifax import StageError, ConnectionError, ValidationError, APIError
try:
receipt = client.attest(...)
except ValidationError as e:
log.error("bad input", error=str(e))
raise # don't retry — caller bug
except StageError as e:
log.error("stage rejected", stage=e.stage, status=e.status_code, error=e.message)
# could be auth issue, or upstream input that doesn't pass the stage check
raise
except ConnectionError as e:
log.warning("transient — retrying", error=str(e))
raise # let your retry layer handle it
except APIError as e:
if 500 <= e.status_code < 600:
log.warning("server error — retrying", status=e.status_code)
raise
log.error("client error", status=e.status_code, error=e.message)
raise
Recommended retry policy
Retry only on ConnectionError and APIError with status_code >= 500. Use exponential backoff. Never retry ValidationError or StageError.
from tenacity import retry, retry_if_exception_type, wait_exponential, stop_after_attempt
@retry(
retry=retry_if_exception_type((ConnectionError,)),
wait=wait_exponential(multiplier=1, min=1, max=30),
stop=stop_after_attempt(5),
)
def attest_with_retries(client, **kwargs):
return client.attest(**kwargs)
