Errors
Non-2xx responses raise typed exceptions, so you can catch the specific failure you care about rather than inspecting HTTP codes. All inherit from HumanBaselinesError.
from humanbaselines import (
AuthenticationError, ValidationError, ServiceUnavailableError,
NotFoundError, APIError,
)
try:
hb.compute(outcome="fatal")
except AuthenticationError: # 401: bad/missing key
...
except ValidationError as e: # 422: e.errors has the field-level detail
print(e.errors)
except ServiceUnavailableError: # 503: warming up (auto-retried first)
...Hierarchy
| Exception | HTTP | Meaning |
|---|---|---|
HumanBaselinesError | any | Base class for everything below. |
APIError | any non-2xx | Fallback when nothing more specific matches. Carries .status_code and .body. |
AuthenticationError | 401 | Missing or invalid API key. |
ValidationError | 422 | Server-side validation failed. .errors holds FastAPI's field-level detail. |
NotFoundError | 404 | Unknown route, or a county/resource that isn't loaded. |
ServiceUnavailableError | 503 | Service warming up or temporarily down. Auto-retried first; if you see it, retries were exhausted. |
AuthenticationError, ValidationError, NotFoundError, and ServiceUnavailableError all subclass APIError, so a single except APIError catches them all while still exposing .status_code and .body.
Client-side validation
Bad filter values are caught before the request: hb.compute(outcome="nope") raises a Pydantic ValidationError locally (no network call), as does an unknown keyword or a bad value in a bound config. To exercise the server's 422 path instead, send a raw dict that bypasses local typing: hb.compute(selections={"outcome": "nope"}).
400 vs 422
A 422 means the request shape was wrong (bad value, unknown field). A 400 (raised as APIError) means a well-formed request the engine couldn't honor: route/depot for a geofence-only county, denominator_vmt="hpms" outside California, or a depot pin out of coverage. See Compute modes.