Skip to content

Enhance generated HTTPException: rich context, parsed error models, configurable handling #100

@dougborg

Description

@dougborg

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

  1. 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)
  2. Include informative __str__ showing method path status and short body snippet.
  3. Recognize multiple success codes derived from spec (all 2XX responses listed) instead of just the first one.
  4. 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.
  5. Expose helper raise_for_status(response, operation_meta) inside generated code to centralize logic.
  6. Optional user hook: allow APIConfig to accept error_handler: Callable[[APIError], None] invoked before raising (for logging / metrics / custom retry).
  7. Backwards compatibility: keep HTTPException name but implement new fields; retain current constructor signature; old code catching HTTPException continues to work.
  8. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions