Summary
Improve the generated client's error handling around non-success HTTP responses. Currently each service method checks for a single expected status code and raises HTTPException(status_code, ' failed with status code: <code>') without context, parsed error model, or headers.
Problems With Current Behavior
- Error message lacks HTTP method, path, expected vs actual status.
- Response body is discarded (critical for debugging validation errors, rate limits, auth issues).
- No attempt to parse documented error schemas (e.g.
ErrorResponse).
- Headers (rate limit, retry, correlation/request IDs) are not surfaced.
- Only one success code is recognized (often 200/201/204 variations in real APIs).
- No hook for custom retry / logging strategies.
Proposed Enhancements
- Rich HTTPException subclass (e.g.
APIError) with attributes:
status_code: int
method: str
path: str
expected_statuses: list[int]
response_headers: dict[str, str]
response_text: str (raw body, truncated to e.g. 2KB)
error_model: Optional[BaseModel] (parsed from first matching error schema if possible)
- Include informative
__str__ showing method path status and short body snippet.
- Recognize multiple success codes derived from spec (all 2XX responses listed) instead of just the first one.
- Attempt to parse error body:
- Inspect documented non-2xx responses in operation.responses.
- If JSON and schema is a $ref, import & parse into the model (pydantic).
- Attach parsed model (or validation error info) to exception.
- Expose helper
raise_for_status(response, operation_meta) inside generated code to centralize logic.
- Optional user hook: allow APIConfig to accept
error_handler: Callable[[APIError], None] invoked before raising (for logging / metrics / custom retry).
- Backwards compatibility: keep
HTTPException name but implement new fields; retain current constructor signature; old code catching HTTPException continues to work.
- Async parity: mirror behavior for async clients.
Stretch (Optional / Future)
- Auto retry on
429 with Retry-After respect (configurable max attempts).
- Deserialize XML or text/plain when declared in spec.
- Structured rate-limit info (limit, remaining, reset) extracted into fields.
Acceptance Criteria
- Generated services raise enriched exception containing method, path, status, and truncated body.
- All documented 2xx codes for an operation are accepted without raising.
- If an error schema is defined (e.g.
ErrorResponse), exception contains parsed model.
- Unit tests cover: single 200, multiple success codes (200/201), error with JSON model, error with invalid JSON, 429 rate limit header extraction.
- Backwards compatibility test: existing code catching
HTTPException still works.
Rationale
Better diagnostics accelerates integration debugging and reduces guesswork when consuming generated clients—especially for large OpenAPI 3.1 specs.
Implementation Sketch
- Add new template snippet for a shared
errors.py in output (or embed in api_config.py for simplicity).
- During service generation, collect all success status codes (keys starting with '2').
- Replace inline status check with call to helper that returns early if response.status_code in expected_codes else raises enriched exception.
- Add conditional parsing of error body using the first matching documented non-2xx response schema with a JSON content type.
Let me know if any constraints/preferences before implementation and I can adjust this plan.
Summary
Improve the generated client's error handling around non-success HTTP responses. Currently each service method checks for a single expected status code and raises
HTTPException(status_code, ' failed with status code: <code>')without context, parsed error model, or headers.Problems With Current Behavior
ErrorResponse).Proposed Enhancements
APIError) with attributes:status_code: intmethod: strpath: strexpected_statuses: list[int]response_headers: dict[str, str]response_text: str(raw body, truncated to e.g. 2KB)error_model: Optional[BaseModel](parsed from first matching error schema if possible)__str__showing method path status and short body snippet.raise_for_status(response, operation_meta)inside generated code to centralize logic.error_handler: Callable[[APIError], None]invoked before raising (for logging / metrics / custom retry).HTTPExceptionname but implement new fields; retain current constructor signature; old code catchingHTTPExceptioncontinues to work.Stretch (Optional / Future)
429withRetry-Afterrespect (configurable max attempts).Acceptance Criteria
ErrorResponse), exception contains parsed model.HTTPExceptionstill works.Rationale
Better diagnostics accelerates integration debugging and reduces guesswork when consuming generated clients—especially for large OpenAPI 3.1 specs.
Implementation Sketch
errors.pyin output (or embed in api_config.py for simplicity).Let me know if any constraints/preferences before implementation and I can adjust this plan.