fix(control-plane): map generated ApiException to typed OtariError#21
Draft
njbrake wants to merge 1 commit into
Draft
fix(control-plane): map generated ApiException to typed OtariError#21njbrake wants to merge 1 commit into
njbrake wants to merge 1 commit into
Conversation
Control-plane resources called the generated client directly, so HTTP errors surfaced as the raw ApiException instead of a typed OtariError (the inference path already maps these in client.py). Extract the mapping into module-level map_api_exception/extract_detail helpers and route every control-plane ergonomic alias through a _translate decorator; the raw escape hatch is left unwrapped. Adds a parametrized unit test across all five resources. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note: this PR was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.
Description
Control-plane resources (
keys,users,budgets,pricing,usage) called the generated client directly, so any HTTP error surfaced as the rawotari._client.exceptions.ApiExceptionrather than a typedotari.errors.OtariError. The inference path already maps generated exceptions inclient.py, so the two surfaces had an inconsistent error contract: a bad master key onkeys.list()raised anApiException(and a full CLI traceback), while the inference path raised a cleanAuthenticationError.This makes the control plane match the inference path:
src/otari/_base.py: extract the existing mapping into module-levelmap_api_exceptionandextract_detailhelpers (behavior-identical move)._BaseOtariClient._map_api_exception/_extract_detailnow delegate to them, so the inference and streaming paths are unchanged.src/otari/control_plane.py: add a small_translatedecorator that catchesApiExceptionand re-raisesmap_api_exception(exc), applied to all 22 ergonomic aliases. Therawgenerated surface is intentionally left unwrapped as the escape hatch.tests/unit/test_control_plane_aliases.py: parametrized test asserting each resource alias surfaces a typedAuthenticationError(not a rawApiException) on a 401, withstatus_codeand detail preserved.No public API change: the ergonomic aliases keep their signatures and return types; only the exception type on failure changes (raw
ApiExceptionbecomes typedOtariError).PR Type
Relevant issues
Fixes #20.
Checklist
tests/unit/test_control_plane_aliases.py).uv run ruff check .,uv run mypy src/,uv run pytest tests/unit).DoD results:
ruffclean,mypy --strictclean (8 files),pytest tests/unit127 passed.AI Usage
AI Model/Tool used: Claude Code (Claude Opus 4.8)
Any additional AI details you'd like to share: Generated via back-and-forth with @njbrake; the diagnosis and decisions are his.