Error Handling โ Fail Loud, Fail Early
openapi-first treats errors as first-class contract documents: every failure mode is a named exception with a stable import pathhare, and every one surfaces as early as possible.
๐งฌ 1. The Hierarchy
All errors derive from OpenAPIFirstError (in openapi_first/errors.py), so a single except OpenAPIFirstError catches every first-party failure:
| Exception | Module | Raised when |
|---|---|---|
OpenAPISpecLoadError |
loader |
Path missing, file unreadable, YAML/JSON invalid, or spec fails OpenAPI 3.x validation |
OpenAPIClientError |
client |
No servers, no paths, missing/duplicate operationId, missing required params at construction |
MissingOperationHandler |
errors |
An operation is declared whose operationId has no matching handler in routes_module |
โฑ๏ธ 2. When Things Fail
The single most important rule: violations are eager, not lazy.
2.1 At application startup
Because these raise during OpenAPIFirstApp(...) construction, CI catches them the moment a spec and its routes drift โ before a single request is served.
2.2 At client construction
- Spec with no
serversโOpenAPIClientError - Spec with no
pathsโOpenAPIClientError - Duplicate
operationIds โOpenAPIClientError(client methods must be unambiguous) - Operation missing
operationIdโOpenAPIClientError
2.3 At call time (client)
Runtime transport errors surface as httpx exceptions (httpx.RequestError family), not swallowed or remapped. Missing required args fail before any HTTP request is made:
๐งฐ 3. Handling in Your App
3.1 Server-side
Handlers raise FastAPI HTTPException for expected operation-level failures (404/422), and the OperationId-binding errors only exist at startup:
3.2 Client-side
httpx.HTTPStatusError isn't raised by the library โ it's the standard httpx.raise_for_status() you can opt into per call. The library never masks a response code.
๐ก๏ธ 4. Fail-Fast Guarantees Recap
| Layer | You write | The library guarantees |
|---|---|---|
| Loader | a spec path | unreadable/invalid specs never reach your app |
| Binder | handler functions | every operation must resolve, or the app won't start |
| Client | a spec | every operationId becomes a callable; missing ones fail at construction |
| Runtime | handler code | FastAPI + Pydantic handle coercion; contract checks already happened at startup |
None of these can silently degrade: a violation is an exception at construction, not a 500 at request time.