Skip to content

feat: add GatewayError for Blaxel gateway-synthesized errors#140

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1777575139-gateway-error-codes
Open

feat: add GatewayError for Blaxel gateway-synthesized errors#140
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1777575139-gateway-error-codes

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 30, 2026

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: platform header), the SDK now raises a GatewayError exception with typed fields:

  • GatewayError exception class with error_code, status_code, retryable, action, do_not, docs_url, and response fields
  • check_gateway_error(response) shared utility that inspects any httpx.Response and raises GatewayError when appropriate
  • 10 string constants in error_codes.py (ROUTE_NOT_FOUND, WORKLOAD_UNAVAILABLE, etc.)
  • Control-plane client integration via httpx event_hooks — all generated API functions automatically raise GatewayError on gateway responses
  • Sandbox action integration — both SandboxAction.handle_response_error() and SyncSandboxAction.handle_response_error() now call check_gateway_error() before checking for general HTTP errors
  • All new symbols exported from blaxel.core

Changes:

  • src/blaxel/core/client/errors.pyGatewayError class + check_gateway_error() utility
  • src/blaxel/core/client/error_codes.py — new file with 10 error code constants
  • src/blaxel/core/client/client.py — httpx event hooks for automatic gateway error detection
  • src/blaxel/core/sandbox/default/action.py — gateway check in handle_response_error()
  • src/blaxel/core/sandbox/sync/action.py — gateway check in handle_response_error()
  • src/blaxel/core/__init__.py — re-export new symbols

Review & Testing Checklist for Human

  • Verify check_gateway_error() correctly parses the gateway JSON body shape ({"error": {"code": "...", "retryable": true, ...}})
  • Confirm httpx event hooks fire before _parse_response in generated API code — the hook should raise GatewayError before the generated code tries to parse the body as a model
  • Test against a live gateway error (e.g. hit a non-existent workload) to confirm GatewayError is raised with correct error_code and retryable values

Notes

  • The check_gateway_error() utility is a no-op for non-gateway responses (returns immediately if X-Blaxel-Source header is absent or not "platform")
  • The sandbox Client (auto-generated in sandbox/client/client.py) is NOT modified — sandbox subsystems that use the generated client pattern go through handle_response_error() which now checks for gateway errors

Link to Devin session: https://app.devin.ai/sessions/90f8d135f6e64ae8b771918c3ceeec6a
Requested by: @Joffref


Note

Adds a GatewayError exception class and check_gateway_error() utility to surface structured errors from the Blaxel gateway proxy. The gateway is identified by the X-Blaxel-Source: platform response header. The check is wired into the control-plane httpx client via event hooks and into sandbox handle_response_error() methods. Ten error code constants are exported from blaxel.core.

Written by Mendral for commit bbd4397.

- 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-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor

@mendral-app mendral-app Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +61 to +62
if response.headers.get("X-Blaxel-Source") != "platform":
return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
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>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3d73de8 — added if response.is_success: return guard so 2xx responses are not raised as GatewayError.

Comment on lines +64 to +72
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
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>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3d73de8 — added response.read() before calling check_gateway_error() in the sync hook so the body is available.

Comment on lines +15 to +16
async def _async_gateway_hook(response: httpx.Response) -> None:
check_gateway_error(response)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
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>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3d73de8 — async hook now calls await response.aread() before check_gateway_error().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant