Skip to content

Commit 75829b6

Browse files
committed
feat: Enhance error handling by adding trace_id to exceptions and formatting messages
1 parent 6381472 commit 75829b6

6 files changed

Lines changed: 87 additions & 31 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,4 @@ cython_debug/
266266

267267
# Test results
268268
results.csv
269+
.ace-tool/

e2b_connect/client.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,19 @@ def make_error_from_http_code(http_code: int):
6161

6262

6363
class ConnectException(Exception):
64-
def __init__(self, status: Code, message: str):
64+
def __init__(self, status: Code, message: str, trace_id: str = None):
6565
self.status = status
6666
self.message = message
67+
self.trace_id = trace_id
68+
super().__init__(self._format_message())
69+
70+
def _format_message(self) -> str:
71+
if self.trace_id:
72+
return f"{self.message} [X-Trace-ID: {self.trace_id}]"
73+
return self.message
74+
75+
def __str__(self) -> str:
76+
return self._format_message()
6777

6878

6979
envelope_header_length = 5
@@ -84,15 +94,28 @@ def decode_envelope_header(header):
8494

8595

8696
def error_for_response(http_resp: Response):
97+
trace_id = None
98+
# Try to get X-Trace-ID from headers
99+
if hasattr(http_resp, 'headers'):
100+
headers = http_resp.headers
101+
if isinstance(headers, (list, tuple)):
102+
# httpcore returns headers as list of tuples
103+
for name, value in headers:
104+
if name.lower() == b'x-trace-id':
105+
trace_id = value.decode('utf-8') if isinstance(value, bytes) else value
106+
break
107+
elif hasattr(headers, 'get'):
108+
trace_id = headers.get("X-Trace-ID") or headers.get("x-trace-id")
109+
87110
try:
88111
error = json.loads(http_resp.content)
89-
return make_error(error)
112+
return make_error(error, trace_id)
90113
except (json.decoder.JSONDecodeError, KeyError):
91114
error = {"code": http_resp.status, "message": http_resp.content.decode("utf-8")}
92-
return make_error(error)
115+
return make_error(error, trace_id)
93116

94117

95-
def make_error(error):
118+
def make_error(error, trace_id: str = None):
96119
status = None
97120
try:
98121
code_value = error.get("code")
@@ -104,7 +127,7 @@ def make_error(error):
104127
except (KeyError, ValueError):
105128
status = Code.unknown
106129

107-
return ConnectException(status, error.get("message", ""))
130+
return ConnectException(status, error.get("message", ""), trace_id)
108131

109132

110133
def _sync_retry(func, exc, retries):

ucloud_sandbox/api/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,26 @@ def handle_api_exception(
4545
except json.JSONDecodeError:
4646
body = {}
4747

48+
# Extract X-Trace-ID from response headers
49+
trace_id = e.headers.get("X-Trace-ID") or e.headers.get("x-trace-id")
50+
4851
if e.status_code == 401:
4952
message = f"{e.status_code}: Unauthorized, please check your credentials."
5053
if body.get("message"):
5154
message += f" - {body['message']}"
52-
return AuthenticationException(message)
55+
return AuthenticationException(message, trace_id=trace_id)
5356

5457
if e.status_code == 429:
5558
message = f"{e.status_code}: Rate limit exceeded, please try again later."
5659
if body.get("message"):
5760
message += f" - {body['message']}"
58-
return RateLimitException(message)
61+
return RateLimitException(message, trace_id=trace_id)
5962

6063
if "message" in body:
6164
return default_exception_class(
62-
f"{e.status_code}: {body['message']}"
65+
f"{e.status_code}: {body['message']}", trace_id=trace_id
6366
).with_traceback(stack_trace)
64-
return default_exception_class(f"{e.status_code}: {e.content}").with_traceback(
67+
return default_exception_class(f"{e.status_code}: {e.content}", trace_id=trace_id).with_traceback(
6568
stack_trace
6669
)
6770

ucloud_sandbox/envd/api.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,33 @@ def handle_envd_api_exception(res: httpx.Response):
2929
return
3030

3131
res.read()
32+
trace_id = res.headers.get("X-Trace-ID") or res.headers.get("x-trace-id")
3233

33-
return format_envd_api_exception(res.status_code, get_message(res))
34+
return format_envd_api_exception(res.status_code, get_message(res), trace_id)
3435

3536

3637
async def ahandle_envd_api_exception(res: httpx.Response):
3738
if res.is_success:
3839
return
3940

4041
await res.aread()
42+
trace_id = res.headers.get("X-Trace-ID") or res.headers.get("x-trace-id")
4143

42-
return format_envd_api_exception(res.status_code, get_message(res))
44+
return format_envd_api_exception(res.status_code, get_message(res), trace_id)
4345

4446

45-
def format_envd_api_exception(status_code: int, message: str):
47+
def format_envd_api_exception(status_code: int, message: str, trace_id: str = None):
4648
if status_code == 400:
47-
return InvalidArgumentException(message)
49+
return InvalidArgumentException(message, trace_id=trace_id)
4850
elif status_code == 401:
49-
return AuthenticationException(message)
51+
return AuthenticationException(message, trace_id=trace_id)
5052
elif status_code == 404:
51-
return NotFoundException(message)
53+
return NotFoundException(message, trace_id=trace_id)
5254
elif status_code == 429:
53-
return SandboxException(f"{message}: The requests are being rate limited.")
55+
return SandboxException(f"{message}: The requests are being rate limited.", trace_id=trace_id)
5456
elif status_code == 502:
55-
return format_sandbox_timeout_exception(message)
57+
return format_sandbox_timeout_exception(message, trace_id)
5658
elif status_code == 507:
57-
return NotEnoughSpaceException(message)
59+
return NotEnoughSpaceException(message, trace_id=trace_id)
5860
else:
59-
return SandboxException(f"{status_code}: {message}")
61+
return SandboxException(f"{status_code}: {message}", trace_id=trace_id)

ucloud_sandbox/envd/rpc.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,32 @@
1919

2020
def handle_rpc_exception(e: Exception):
2121
if isinstance(e, ConnectException):
22+
trace_id = getattr(e, "trace_id", None)
2223
if e.status == Code.invalid_argument:
23-
return InvalidArgumentException(e.message)
24+
return InvalidArgumentException(e.message, trace_id=trace_id)
2425
elif e.status == Code.unauthenticated:
25-
return AuthenticationException(e.message)
26+
return AuthenticationException(e.message, trace_id=trace_id)
2627
elif e.status == Code.not_found:
27-
return NotFoundException(e.message)
28+
return NotFoundException(e.message, trace_id=trace_id)
2829
elif e.status == Code.unavailable:
29-
return format_sandbox_timeout_exception(e.message)
30+
return format_sandbox_timeout_exception(e.message, trace_id=trace_id)
3031
elif e.status == Code.resource_exhausted:
3132
return RateLimitException(
32-
f"{e.message}: Rate limit exceeded, please try again later."
33+
f"{e.message}: Rate limit exceeded, please try again later.",
34+
trace_id=trace_id,
3335
)
3436
elif e.status == Code.canceled:
3537
return TimeoutException(
36-
f"{e.message}: This error is likely due to exceeding 'request_timeout'. You can pass the request timeout value as an option when making the request."
38+
f"{e.message}: This error is likely due to exceeding 'request_timeout'. You can pass the request timeout value as an option when making the request.",
39+
trace_id=trace_id,
3740
)
3841
elif e.status == Code.deadline_exceeded:
3942
return TimeoutException(
40-
f"{e.message}: This error is likely due to exceeding 'timeout' — the total time a long running request (like process or directory watch) can be active. It can be modified by passing 'timeout' when making the request. Use '0' to disable the timeout."
43+
f"{e.message}: This error is likely due to exceeding 'timeout' — the total time a long running request (like process or directory watch) can be active. It can be modified by passing 'timeout' when making the request. Use '0' to disable the timeout.",
44+
trace_id=trace_id,
4145
)
4246
else:
43-
return SandboxException(f"{e.status}: {e.message}")
47+
return SandboxException(f"{e.status}: {e.message}", trace_id=trace_id)
4448
else:
4549
return e
4650

ucloud_sandbox/exceptions.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
def format_sandbox_timeout_exception(message: str):
1+
def format_sandbox_timeout_exception(message: str, trace_id: str = None):
22
return TimeoutException(
3-
f"{message}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeout' when starting the sandbox or calling '.set_timeout' on the sandbox with the desired timeout."
3+
f"{message}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeout' when starting the sandbox or calling '.set_timeout' on the sandbox with the desired timeout.",
4+
trace_id=trace_id,
45
)
56

67

@@ -23,7 +24,18 @@ class SandboxException(Exception):
2324
Raised when a general sandbox exception occurs.
2425
"""
2526

26-
pass
27+
def __init__(self, message: str = "", trace_id: str = None):
28+
self.message = message
29+
self.trace_id = trace_id
30+
super().__init__(self._format_message())
31+
32+
def _format_message(self) -> str:
33+
if self.trace_id:
34+
return f"{self.message} [X-Trace-ID: {self.trace_id}]"
35+
return self.message
36+
37+
def __str__(self) -> str:
38+
return self._format_message()
2739

2840

2941
class TimeoutException(SandboxException):
@@ -68,7 +80,18 @@ class AuthenticationException(Exception):
6880
Raised when authentication fails.
6981
"""
7082

71-
pass
83+
def __init__(self, message: str = "", trace_id: str = None):
84+
self.message = message
85+
self.trace_id = trace_id
86+
super().__init__(self._format_message())
87+
88+
def _format_message(self) -> str:
89+
if self.trace_id:
90+
return f"{self.message} [X-Trace-ID: {self.trace_id}]"
91+
return self.message
92+
93+
def __str__(self) -> str:
94+
return self._format_message()
7295

7396

7497
class TemplateException(SandboxException):

0 commit comments

Comments
 (0)