Conversation
handle_api_exception and envd get_message assumed a JSON error body is an
object: body["message"] raises TypeError when the body is null/a number, and
e.json().get(...) raises AttributeError when it is any non-dict (string,
array, scalar) -- neither caught by the surrounding JSONDecodeError guard.
A proxy or gateway in front of the control plane or envd can return an error
with Content-Type: application/json and a bare string or scalar body, which
then masks the real HTTP error with an opaque TypeError/AttributeError from
deep in the SDK. The JS SDK already tolerates these bodies (ApiError is typed
{ message?: string } | string). Guard for non-object bodies and fall back the
same way, so the HTTP error still maps to a SandboxException.
|
We require contributors to sign our Contributor License Agreement, and we don't have @winklemad on file. You can sign our CLA at https://e2b.dev/docs/cla . Once you've signed, post a comment here that says '@cla-bot check' |
🦋 Changeset detectedLatest commit: c3e9527 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
TASTE.md review: checked T-1 (JS parity for non-object error bodies — matches res.error?.message ?? res.error / typeof res.error === 'string'), T-17 (widest input / isinstance grouping), T-57/T-60 (errors stay in the SandboxException hierarchy, mapping remains centralized in api/__init__.py and envd/api.py), T-62 (message preserves HTTP status and body text). 0 violations — the change complies.
Non-blocking observation: handle_api_exception treats a JSON "" body as no message (falls back to status: content), while envd.api.get_message returns the empty string for the same body. Not a TASTE rule, but aligning the two would keep the API/envd paths symmetric.
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
Per review: an empty JSON string body now falls back to the raw text (as handle_api_exception already does) instead of surfacing an empty message, keeping the API and envd error paths symmetric.
|
Good catch on the asymmetry — aligned it in c3e9527: |
The bug
Both Python error mappers assume a JSON error body is an object, so a body that is valid JSON but not an object crashes the mapper instead of producing a
SandboxException:e2b/api/__init__.py(handle_api_exception):body["message"] if "message" in body else NoneraisesTypeError: argument of type 'NoneType'/'int' is not iterablewhen the body isnull, a number, or a boolean.e2b/envd/api.py(get_message):e.json().get("message", e.text)raisesAttributeError: '…' object has no attribute 'get'when the body is any non-dict (a JSON string, array, or scalar). The surroundingexcept json.JSONDecodeErrordoes not catch it.Why it matters
Every control-plane call (
Sandbox.create/kill/set_timeout/get_info/get_metrics,list().next_items(), and async equivalents) routes errors through site 1; everysandbox.files.read/write/listroutes through site 2. The trigger is realistic: a reverse proxy, gateway, or CDN in front of the control plane or envd can return an error withContent-Type: application/jsonand a bare string or scalar body (e.g."upstream connect error",null) — common during an upstream outage. When that happens, instead of a clearSandboxException/TimeoutExceptionwith the HTTP status, the developer gets an opaqueTypeError/AttributeErrorfrom deep inside the SDK, masking the actual failure.This is a JS↔Python divergence: the JS SDK deliberately tolerates non-object bodies —
handleApiErrorusesresponse.error?.message ?? response.error, andhandleEnvdApiErrorusestypeof res.error === 'string' ? res.error : res.error?.message, withApiErrortyped{ message?: string } | string. The Python mirror was written assuming a dict.Fix
Guard both sites for a non-object body and fall back the same way the JS SDK does (use a string body as the message; otherwise fall back to the raw text /
None), so the HTTP error still maps to aSandboxException.Tests / validation
Added regression tests to
tests/test_api_exception.py(non-object bodiesnull/42/true→SandboxExceptionwith the status) andtests/test_envd_api_exception.py(get_messageon a JSON string and array body; a non-dict envd error mapping without crashing).pytestgreen (23),black+ruffclean, and a@e2b/python-sdkpatch changeset is included.