Skip to content

Commit 44dcedc

Browse files
committed
feat: add timeout_type/timeoutType field to TimeoutException/TimeoutError
Fixes #463. Users can now distinguish between sandbox, request, and execution timeouts by checking the type field instead of parsing error messages. Python SDK: TimeoutException gains a `timeout_type` attribute (str | None) set to "sandbox_timeout", "request_timeout", or "execution_timeout" at every construction site. JS SDK: TimeoutError gains a readonly `timeoutType` property (TimeoutType | undefined) with the same three values. A new `TimeoutType` union type is exported from the package. AI Disclosure: this PR was authored by Claude Opus 4.6 (an AI).
1 parent d289772 commit 44dcedc

File tree

5 files changed

+66
-16
lines changed

5 files changed

+66
-16
lines changed

packages/js-sdk/src/envd/rpc.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@ export function handleRpcError(err: unknown): Error {
2626
return formatSandboxTimeoutError(err.message)
2727
case Code.Canceled:
2828
return new TimeoutError(
29-
`${err.message}: This error is likely due to exceeding 'requestTimeoutMs'. You can pass the request timeout value as an option when making the request.`
29+
\`\${err.message}: This error is likely due to exceeding 'requestTimeoutMs'. You can pass the request timeout value as an option when making the request.\`,
30+
undefined,
31+
'request_timeout'
3032
)
3133
case Code.DeadlineExceeded:
3234
return new TimeoutError(
33-
`${err.message}: This error is likely due to exceeding 'timeoutMs' — the total time a long running request (like command execution or directory watch) can be active. It can be modified by passing 'timeoutMs' when making the request. Use '0' to disable the timeout.`
35+
\`\${err.message}: This error is likely due to exceeding 'timeoutMs' — the total time a long running request (like command execution or directory watch) can be active. It can be modified by passing 'timeoutMs' when making the request. Use '0' to disable the timeout.\`,
36+
undefined,
37+
'execution_timeout'
3438
)
3539
default:
36-
return new SandboxError(`${err.code}: ${err.message}`)
40+
return new SandboxError(\`\${err.code}: \${err.message}\`)
3741
}
3842
}
3943

@@ -68,9 +72,9 @@ export function authenticationHeader(
6872
return {}
6973
}
7074

71-
const value = `${username}:`
75+
const value = \`\${username}:\`
7276

7377
const encoded = encode64(value)
7478

75-
return { Authorization: `Basic ${encoded}` }
76-
}
79+
return { Authorization: \`Basic \${encoded}\` }
80+
}

packages/js-sdk/src/errors.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
1+
/**
2+
* The type of timeout that occurred.
3+
*
4+
* - `"sandbox_timeout"` – the sandbox itself timed out (idle / max lifetime).
5+
* - `"request_timeout"` – a single HTTP / RPC request exceeded its deadline.
6+
* - `"execution_timeout"` – a long-running operation (process, watch, etc.) exceeded its allowed duration.
7+
*/
8+
export type TimeoutType =
9+
| 'sandbox_timeout'
10+
| 'request_timeout'
11+
| 'execution_timeout'
12+
113
// This is the message for the sandbox timeout error when the response code is 502/Unavailable
214
export function formatSandboxTimeoutError(message: string) {
315
return new TimeoutError(
4-
`${message}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeoutMs' when starting the sandbox or calling '.setTimeout' on the sandbox with the desired timeout.`
16+
\`\${message}: This error is likely due to sandbox timeout. You can modify the sandbox timeout by passing 'timeoutMs' when starting the sandbox or calling '.setTimeout' on the sandbox with the desired timeout.\`,
17+
undefined,
18+
'sandbox_timeout'
519
)
620
}
721

@@ -30,11 +44,29 @@ export class SandboxError extends Error {
3044
* The [deadline_exceeded] error type is caused by exceeding the timeout for command execution, watch, etc.
3145
*
3246
* The [unknown] error type is sometimes caused by the sandbox timeout when the request is not processed correctly.
47+
*
48+
* Use the {@link timeoutType} property to determine which kind of timeout occurred
49+
* without having to parse the error message.
3350
*/
3451
export class TimeoutError extends SandboxError {
35-
constructor(message: string, stackTrace?: string) {
52+
/**
53+
* Indicates which kind of timeout occurred.
54+
*
55+
* - \`"sandbox_timeout"\` – the sandbox itself timed out (idle / max lifetime).
56+
* - \`"request_timeout"\` – a single HTTP / RPC request exceeded its deadline.
57+
* - \`"execution_timeout"\` – a long-running operation (process, watch, etc.) exceeded its allowed duration.
58+
* - \`undefined\` the timeout type could not be determined.
59+
*/
60+
readonly timeoutType?: TimeoutType
61+
62+
constructor(
63+
message: string,
64+
stackTrace?: string,
65+
timeoutType?: TimeoutType
66+
) {
3667
super(message, stackTrace)
3768
this.name = 'TimeoutError'
69+
this.timeoutType = timeoutType
3870
}
3971
}
4072

@@ -139,4 +171,4 @@ export class FileUploadError extends BuildError {
139171
super(message, stackTrace)
140172
this.name = 'FileUploadError'
141173
}
142-
}
174+
}

packages/js-sdk/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export {
1717
BuildError,
1818
FileUploadError,
1919
} from './errors'
20+
export type { TimeoutType } from './errors'
2021
export type { Logger } from './logs'
2122

2223
export { getSignature } from './sandbox/signature'
@@ -109,4 +110,4 @@ export {
109110
LogEntryEnd,
110111
type LogEntryLevel,
111112
defaultBuildLogger,
112-
} from './template/logger'
113+
} from './template/logger'

packages/python-sdk/e2b/envd/rpc.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ def handle_rpc_exception(e: Exception):
3333
)
3434
elif e.status == Code.canceled:
3535
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."
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.",
37+
timeout_type="request_timeout",
3738
)
3839
elif e.status == Code.deadline_exceeded:
3940
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."
41+
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.",
42+
timeout_type="execution_timeout",
4143
)
4244
else:
4345
return SandboxException(f"{e.status}: {e.message}")
@@ -58,4 +60,4 @@ def authentication_header(
5860

5961
encoded = base64.b64encode(value.encode("utf-8")).decode("utf-8")
6062

61-
return {"Authorization": f"Basic {encoded}"}
63+
return {"Authorization": f"Basic {encoded}"}

packages/python-sdk/e2b/exceptions.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
def format_sandbox_timeout_exception(message: str):
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+
timeout_type="sandbox_timeout",
45
)
56

67

78
def format_request_timeout_error() -> Exception:
89
return TimeoutException(
910
"Request timed out — the 'request_timeout' option can be used to increase this timeout",
11+
timeout_type="request_timeout",
1012
)
1113

1214

1315
def format_execution_timeout_error() -> Exception:
1416
return TimeoutException(
1517
"Execution timed out — the 'timeout' option can be used to increase this timeout",
18+
timeout_type="execution_timeout",
1619
)
1720

1821

@@ -34,9 +37,17 @@ class TimeoutException(SandboxException):
3437
The `canceled` exception type is caused by exceeding request timeout.\n
3538
The `deadline_exceeded` exception type is caused by exceeding the timeout for process, watch, etc.\n
3639
The `unknown` exception type is sometimes caused by the sandbox timeout when the request is not processed correctly.\n
40+
41+
The ``timeout_type`` attribute indicates which kind of timeout occurred:
42+
43+
- ``"sandbox_timeout"`` – the sandbox itself timed out (idle / max lifetime).
44+
- ``"request_timeout"`` – a single HTTP / RPC request exceeded its deadline.
45+
- ``"execution_timeout"`` – a long-running operation (process, watch, …) exceeded its allowed duration.
3746
"""
3847

39-
pass
48+
def __init__(self, message: str = "", timeout_type: str | None = None):
49+
super().__init__(message)
50+
self.timeout_type = timeout_type
4051

4152

4253
class InvalidArgumentException(SandboxException):
@@ -108,4 +119,4 @@ class BuildException(Exception):
108119
class FileUploadException(BuildException):
109120
"""
110121
Raised when the file upload fails.
111-
"""
122+
"""

0 commit comments

Comments
 (0)