Repository navigation
Enhance generated HTTPException: rich context, parsed error models, configurable handling #100
Copy link
Copy link
Open
Description
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: 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)
- 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
HTTPExceptionname but implement new fields; retain current constructor signature; old code catchingHTTPExceptioncontinues to work. - Async parity: mirror behavior for async clients.
Stretch (Optional / Future)
- Auto retry on
429withRetry-Afterrespect (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
HTTPExceptionstill 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.pyin 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.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
Add design document for Issue MarcoMuellner#100: Enhanced error handling
Add static file infrastructure for generated clients
dougborg commentedon Oct 27, 2025
Implementation Roadmap
A comprehensive design document has been created and is now under review in #124. Based on that design, here's the proposed implementation roadmap:
🎯 Phase 0: Foundation (In Progress)
PR #125 - Static File Infrastructure ✅
StaticFilemodel andConversionResult.static_filesfieldstatic_file_generator.pymodulewrite_data()PR #124 - Design Document 📋
📋 Proposed Implementation Issues
Once the design is approved (#124) and infrastructure is merged (#125), we'll create the following issues:
Issue: Core Exception Hierarchy
Estimated Effort: Small (1-2 days)
Depends On: #125
Implement the base exception types:
errors.pystatic file withAPIError,ClientError,ServerErrorHTTPExceptionbackwards-compatible alias__str__with context display and body truncationAcceptance Criteria:
Issue: Response Wrapper & Utilities
Estimated Effort: Medium (2-3 days)
Depends On: Core Exception Hierarchy
Implement
DetailedResponseand helper utilities:response.pywithDetailedResponse[T]generic classon(),otherwise(),resolve()utils.pywithunwrap(),expect(),match()has_error_model(),UnexpectedStatusErrorAcceptance Criteria:
DetailedResponsesupports generic types and fluent APIIssue: Service Generator - Success Code Collection
Estimated Effort: Medium (2-3 days)
Depends On: Core Exception Hierarchy
Update service generator to collect all success codes:
_collect_success_codes()for exact codes and wildcards_collect_error_models()to extract error schemas_collect_response_models()for success response modelsOpReturnTypemodel with new metadata fieldsAcceptance Criteria:
$refschemasIssue: Error Creation Functions
Estimated Effort: Medium-Large (3-4 days)
Depends On: Service Generator - Success Code Collection
Implement error creation with model parsing:
_create_error_from_response()for httpx/requests_create_error_from_response_async()for aiohttpmodel_validate_json()Acceptance Criteria:
Issue: Simple API Generation
Estimated Effort: Large (4-5 days)
Depends On: Error Creation Functions
Generate simple API functions (direct model returns):
Acceptance Criteria:
Issue: Detailed API Generation
Estimated Effort: Large (4-5 days)
Depends On: Simple API Generation
Generate detailed API functions with response wrappers:
_detailed()function variants for all operationsDetailedResponse[T]for same-model responsesDetailedResponse[Union[A, B]]for different-model responsesAcceptance Criteria:
DetailedResponsewrapperon().otherwise().resolve()) works end-to-endIssue: Type Guards for Union Responses
Estimated Effort: Small-Medium (2-3 days)
Depends On: Detailed API Generation
Generate type guard functions for Union responses:
is_<model>()type guard for each response model in UnionTypeGuardfromtyping_extensionsfor Pydantic 1 compatibilityAcceptance Criteria:
Issue: Integration Testing & Validation
Estimated Effort: Medium (3-4 days)
Depends On: Type Guards, Detailed API Generation
Comprehensive end-to-end testing:
Acceptance Criteria:
Issue: Documentation & Examples
Estimated Effort: Medium (2-3 days)
Depends On: Integration Testing
Complete user-facing documentation:
unwrap,expect,match)Acceptance Criteria:
📊 Overall Progress Tracker
🎯 Next Steps
⏱️ Estimated Timeline
Total Estimated Duration: 8-10 weeks for full implementation
💬 Feedback Welcome
Please review the design document (#124) and provide feedback on:
Let's discuss any concerns or suggestions before moving forward with implementation!