Skip to content
Fuse
Esc
navigateopen⌘Jpreview
On this page

Errors

ApiError, error codes, and the is_* predicates

Every SDK method that talks to the orchestrator can raise. Non-2xx responses raise fuse.ApiError, built from the server’s structured error envelope; anything else (a network failure, a body that fails to validate against a pydantic model) raises the underlying httpx/pydantic exception directly. Branch on the code, not the message:

try:
    client.environments.get("missing")
except Exception as exc:
    if fuse.is_not_found(exc):
        ...  # handle the 404 case
    else:
        raise

fuse.as_api_error(exc) gets you the full object when you need more than a yes/no check, status, code, request_id (for correlating with server-side logs), and body:

api_err = fuse.as_api_error(exc)
if api_err:
    print(f"status={api_err.status} code={api_err.code} request_id={api_err.request_id}")

Code-checking predicates

def is_not_found(err: object) -> bool: ...
def is_conflict(err: object) -> bool: ...
def is_unauthorized(err: object) -> bool: ...
def is_invalid_argument(err: object) -> bool: ...
def is_unavailable(err: object) -> bool: ...

Each reports whether err is (or wraps) an ApiError with the matching code. Prefer these over comparing .code strings directly, and definitely over parsing str(err), which is meant for logs, not branching logic.

The server also returns a sixth code, unimplemented (501, when a provider has no guest to exec into), with no matching is_* predicate yet, check api_err.code == "unimplemented" directly until one is added.

ApiError

class ApiError(Exception):
    status: int
    code: str
    message: str
    details: dict[str, str]
    request_id: str
    body: bytes

Full code list

Constant Wire value Typical status
CODE_NOT_FOUND not_found 404
CODE_CONFLICT conflict 409
CODE_INVALID_ARGUMENT invalid_argument 400
CODE_UNAUTHORIZED unauthorized 401, 403, or 502
CODE_UNAVAILABLE unavailable 503
CODE_INTERNAL internal 500 or 502
(none yet) unimplemented 501
(none yet) payload_too_large 413

A 502 with code unauthorized is the one case that isn’t about your own token: it means the host agent rejected the per-host token during host registration, so the value to fix is that host’s FC_AGENT_TOKEN, not the token the client was constructed with.

check_response and parse_api_error

def check_response(response: httpx.Response) -> None: ...
def parse_api_error(status: int, headers: httpx.Headers, body: bytes) -> ApiError: ...

The lower-level functions every service method calls internally. You won’t normally call these directly.

Was this page helpful?