Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.devhelm.io/llms.txt

Use this file to discover all available pages before exploring further.

The SDK raises typed exceptions for API failures, validation issues, and network problems. The taxonomy mirrors the API’s ErrorResponse envelope, with subclasses per HTTP class for ergonomic catching.

Exception hierarchy

DevhelmError                  ← umbrella; catch as a fallback
├── DevhelmValidationError    ← local request/response shape validation
├── DevhelmTransportError     ← connection refused, DNS, TLS, timeout
└── DevhelmApiError           ← non-2xx HTTP response
    ├── DevhelmAuthError      ← 401, 403
    ├── DevhelmNotFoundError  ← 404
    ├── DevhelmConflictError  ← 409
    ├── DevhelmRateLimitError ← 429
    └── DevhelmServerError    ← 5xx
All classes are importable from devhelm:
from devhelm import (
    DevhelmError,
    DevhelmValidationError,
    DevhelmApiError,
    DevhelmAuthError,
    DevhelmNotFoundError,
    DevhelmConflictError,
    DevhelmRateLimitError,
    DevhelmServerError,
    DevhelmTransportError,
)

DevhelmApiError

The most common error class — raised for any non-2xx HTTP response.
AttributeTypeDescription
statusintHTTP status code (e.g. 404)
codestrCoarse machine-readable category from ErrorResponse.code (e.g. "NOT_FOUND"). Defaults to "API_ERROR" if the server didn’t supply one.
messagestrHuman-readable message — for logs and user-facing copy
detailstr | NoneAdditional context, when available
bodydict | str | NoneRaw parsed response body for debugging
request_idstr | NonePer-request id from the X-Request-Id response header. Always include in support tickets.
from devhelm import DevhelmApiError

try:
    client.monitors.get("missing")
except DevhelmApiError as e:
    print(e.status)       # 404
    print(e.code)         # "NOT_FOUND"
    print(e.request_id)   # "req_01HJV..."
    print(e.message)

DevhelmValidationError

Raised before HTTP I/O when a request body fails Pydantic validation, and after a successful response when the response body doesn’t match the expected schema (rare — usually indicates a stale SDK).
AttributeTypeDescription
messagestrWhat failed validation
errorslistPydantic-style error records with loc, msg, type
from devhelm import DevhelmValidationError

try:
    client.monitors.create({"type": "HTTP"})  # missing required fields
except DevhelmValidationError as e:
    for err in e.errors:
        print(f"{err['loc']}: {err['msg']}")

DevhelmTransportError

Wraps httpx network failures (connection refused, DNS, TLS handshake, timeout) so callers don’t need to import httpx. The original exception is on __cause__.
from devhelm import DevhelmTransportError

try:
    client.monitors.list()
except DevhelmTransportError as e:
    print(f"Network error: {e}")
    print(f"Underlying: {e.__cause__!r}")

Handling patterns

Catch by HTTP class

from devhelm import (
    DevhelmAuthError, DevhelmNotFoundError, DevhelmRateLimitError, DevhelmError,
)

try:
    monitor = client.monitors.get(monitor_id)
except DevhelmAuthError:
    print("Bad token or insufficient permissions")
except DevhelmNotFoundError:
    print(f"Monitor {monitor_id} no longer exists")
except DevhelmRateLimitError as e:
    print(f"Rate limited (request_id={e.request_id})")
except DevhelmError:
    raise

Retry on rate limits and 5xx

import time
from devhelm import DevhelmRateLimitError, DevhelmServerError, DevhelmTransportError

def with_retry(fn, *, max_retries=3, base_delay=1.0):
    for attempt in range(max_retries + 1):
        try:
            return fn()
        except (DevhelmRateLimitError, DevhelmServerError, DevhelmTransportError) as e:
            if attempt == max_retries:
                raise
            delay = base_delay * (2 ** attempt)
            time.sleep(delay)

monitors = with_retry(client.monitors.list)
Don’t retry on DevhelmValidationError, DevhelmAuthError, DevhelmNotFoundError, or DevhelmConflictError — these are deterministic and won’t change without code or config changes.

Deploy lock contention

from devhelm import DevhelmConflictError

try:
    lock = client.deploy_lock.acquire({
        "lockedBy": "ci-pipeline",
        "ttlMinutes": 30,
    })
except DevhelmConflictError:
    current = client.deploy_lock.current()
    if current:
        print(f"Lock held by {current.lockedBy} (since {current.acquiredAt})")

Catch-all logging

from devhelm import DevhelmApiError, DevhelmError

try:
    do_work(client)
except DevhelmApiError as e:
    log.error(
        "DevHelm API error",
        extra={
            "status": e.status,
            "code": e.code,
            "request_id": e.request_id,
            "detail": e.detail,
        },
    )
except DevhelmError:
    log.exception("DevHelm SDK error")

Next steps

Client reference

Full method reference for all resources.

Error patterns

Common error scenarios across all surfaces.