feat: add GatewayError for Blaxel gateway-synthesized errors#140
feat: add GatewayError for Blaxel gateway-synthesized errors#140devin-ai-integration[bot] wants to merge 1 commit into
Conversation
- Add GatewayError exception class with error_code, retryable, action, do_not, docs_url fields parsed from gateway response headers/body - Add check_gateway_error() shared utility for consistent detection - Add error_codes.py with all 10 stable gateway error code constants - Hook into control-plane Client via httpx event_hooks so all generated API functions automatically raise GatewayError on gateway responses - Add check_gateway_error() to SandboxAction.handle_response_error() and SyncSandboxAction.handle_response_error() for sandbox HTTP calls - Export GatewayError, check_gateway_error, and all error code constants from blaxel.core Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
High risk — 3 issues across 2 files
Three critical bugs make this PR non-functional as written. The event hook calls response.json() before the body is read (httpx fires response hooks before response.read() in Client.send), which will always raise httpx.ResponseNotRead. The async hook is sync-safe only by accident — it needs await response.aread() before calling the shared utility. And check_gateway_error() has no status-code guard, so any 2xx response with X-Blaxel-Source: platform will incorrectly raise GatewayError.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<assessment>
Three critical bugs make this PR non-functional as written. The event hook calls `response.json()` before the body is read (httpx fires response hooks before `response.read()` in `Client.send`), which will always raise `httpx.ResponseNotRead`. The async hook is sync-safe only by accident — it needs `await response.aread()` before calling the shared utility. And `check_gateway_error()` has no status-code guard, so any 2xx response with `X-Blaxel-Source: platform` will incorrectly raise `GatewayError`.
</assessment>
<file name="src/blaxel/core/client/errors.py">
<issue location="src/blaxel/core/client/errors.py:61">
`check_gateway_error()` raises for every response with `X-Blaxel-Source: platform`, including 2xx. Successful gateway responses will incorrectly raise `GatewayError`.
</issue>
<issue location="src/blaxel/core/client/errors.py:64">
httpx fires response event hooks before the body is read (`response.read()` is called later in `Client.send`). Calling `response.json()` here always raises `httpx.ResponseNotRead`. The body must be read explicitly inside the hook.
</issue>
</file>
<file name="src/blaxel/core/client/client.py">
<issue location="src/blaxel/core/client/client.py:15">
The async response hook must `await response.aread()` before accessing the body. The shared `check_gateway_error()` calls `response.read()` (sync), which does not read the body in an async context and will raise `ResponseNotRead`.
</issue>
</file>
Tag @mendral-app with feedback or questions. View session
| if response.headers.get("X-Blaxel-Source") != "platform": | ||
| return |
There was a problem hiding this comment.
bug (P0): check_gateway_error() raises for every response with X-Blaxel-Source: platform, including 2xx. Successful gateway responses will incorrectly raise GatewayError.
Suggested change
| if response.headers.get("X-Blaxel-Source") != "platform": | |
| return | |
| if response.headers.get("X-Blaxel-Source") != "platform": | |
| return | |
| if response.is_success: | |
| return |
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/blaxel/core/client/errors.py, line 61:
<issue>
`check_gateway_error()` raises for every response with `X-Blaxel-Source: platform`, including 2xx. Successful gateway responses will incorrectly raise `GatewayError`.
</issue>
There was a problem hiding this comment.
Fixed in 3d73de8 — added if response.is_success: return guard so 2xx responses are not raised as GatewayError.
| error_obj: dict[str, Any] = {} | ||
| try: | ||
| body = response.json() | ||
| if isinstance(body, dict): | ||
| error_obj = body.get("error", {}) | ||
| if not isinstance(error_obj, dict): | ||
| error_obj = {} | ||
| except (json.JSONDecodeError, ValueError): | ||
| pass |
There was a problem hiding this comment.
bug (P0): httpx fires response event hooks before the body is read (response.read() is called later in Client.send). Calling response.json() here always raises httpx.ResponseNotRead. The body must be read explicitly inside the hook.
Suggested change
| error_obj: dict[str, Any] = {} | |
| try: | |
| body = response.json() | |
| if isinstance(body, dict): | |
| error_obj = body.get("error", {}) | |
| if not isinstance(error_obj, dict): | |
| error_obj = {} | |
| except (json.JSONDecodeError, ValueError): | |
| pass | |
| error_obj: dict[str, Any] = {} | |
| try: | |
| response.read() | |
| body = response.json() | |
| if isinstance(body, dict): | |
| error_obj = body.get("error", {}) | |
| if not isinstance(error_obj, dict): | |
| error_obj = {} | |
| except (json.JSONDecodeError, ValueError): | |
| pass |
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/blaxel/core/client/errors.py, line 64:
<issue>
httpx fires response event hooks before the body is read (`response.read()` is called later in `Client.send`). Calling `response.json()` here always raises `httpx.ResponseNotRead`. The body must be read explicitly inside the hook.
</issue>
There was a problem hiding this comment.
Fixed in 3d73de8 — added response.read() before calling check_gateway_error() in the sync hook so the body is available.
| async def _async_gateway_hook(response: httpx.Response) -> None: | ||
| check_gateway_error(response) |
There was a problem hiding this comment.
bug (P0): The async response hook must await response.aread() before accessing the body. The shared check_gateway_error() calls response.read() (sync), which does not read the body in an async context and will raise ResponseNotRead.
Suggested change
| async def _async_gateway_hook(response: httpx.Response) -> None: | |
| check_gateway_error(response) | |
| async def _async_gateway_hook(response: httpx.Response) -> None: | |
| await response.aread() | |
| check_gateway_error(response) |
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/blaxel/core/client/client.py, line 15:
<issue>
The async response hook must `await response.aread()` before accessing the body. The shared `check_gateway_error()` calls `response.read()` (sync), which does not read the body in an async context and will raise `ResponseNotRead`.
</issue>
There was a problem hiding this comment.
Fixed in 3d73de8 — async hook now calls await response.aread() before check_gateway_error().
Summary
Adds structured gateway error support to the Python SDK. When the Blaxel gateway proxy synthesizes an error response (identified by the
X-Blaxel-Source: platformheader), the SDK now raises aGatewayErrorexception with typed fields:GatewayErrorexception class witherror_code,status_code,retryable,action,do_not,docs_url, andresponsefieldscheck_gateway_error(response)shared utility that inspects anyhttpx.Responseand raisesGatewayErrorwhen appropriateerror_codes.py(ROUTE_NOT_FOUND,WORKLOAD_UNAVAILABLE, etc.)event_hooks— all generated API functions automatically raiseGatewayErroron gateway responsesSandboxAction.handle_response_error()andSyncSandboxAction.handle_response_error()now callcheck_gateway_error()before checking for general HTTP errorsblaxel.coreChanges:
src/blaxel/core/client/errors.py—GatewayErrorclass +check_gateway_error()utilitysrc/blaxel/core/client/error_codes.py— new file with 10 error code constantssrc/blaxel/core/client/client.py— httpx event hooks for automatic gateway error detectionsrc/blaxel/core/sandbox/default/action.py— gateway check inhandle_response_error()src/blaxel/core/sandbox/sync/action.py— gateway check inhandle_response_error()src/blaxel/core/__init__.py— re-export new symbolsReview & Testing Checklist for Human
check_gateway_error()correctly parses the gateway JSON body shape ({"error": {"code": "...", "retryable": true, ...}})_parse_responsein generated API code — the hook should raiseGatewayErrorbefore the generated code tries to parse the body as a modelGatewayErroris raised with correcterror_codeandretryablevaluesNotes
check_gateway_error()utility is a no-op for non-gateway responses (returns immediately ifX-Blaxel-Sourceheader is absent or not"platform")Client(auto-generated insandbox/client/client.py) is NOT modified — sandbox subsystems that use the generated client pattern go throughhandle_response_error()which now checks for gateway errorsLink to Devin session: https://app.devin.ai/sessions/90f8d135f6e64ae8b771918c3ceeec6a
Requested by: @Joffref
Note
Adds a
GatewayErrorexception class andcheck_gateway_error()utility to surface structured errors from the Blaxel gateway proxy. The gateway is identified by theX-Blaxel-Source: platformresponse header. The check is wired into the control-plane httpx client via event hooks and into sandboxhandle_response_error()methods. Ten error code constants are exported fromblaxel.core.Written by Mendral for commit bbd4397.