diff --git a/packages/js-sdk/src/envd/rpc.ts b/packages/js-sdk/src/envd/rpc.ts index 9ad34ec6e8..eca96523de 100644 --- a/packages/js-sdk/src/envd/rpc.ts +++ b/packages/js-sdk/src/envd/rpc.ts @@ -26,11 +26,15 @@ export function handleRpcError(err: unknown): Error { return formatSandboxTimeoutError(err.message) case Code.Canceled: return new TimeoutError( - `${err.message}: This error is likely due to exceeding 'requestTimeoutMs'. You can pass the request timeout value as an option when making the request.` + `${err.message}: This error is likely due to exceeding 'requestTimeoutMs'. You can pass the request timeout value as an option when making the request.`, + undefined, + 'request_timeout' ) case Code.DeadlineExceeded: return new TimeoutError( - `${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.` + `${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.`, + undefined, + 'execution_timeout' ) default: return new SandboxError(`${err.code}: ${err.message}`) diff --git a/packages/js-sdk/src/errors.ts b/packages/js-sdk/src/errors.ts index ad51c1f2a4..7d0bf25110 100644 --- a/packages/js-sdk/src/errors.ts +++ b/packages/js-sdk/src/errors.ts @@ -1,7 +1,21 @@ +/** + * The type of timeout that occurred. + * + * - `"sandbox_timeout"` – the sandbox itself timed out (idle / max lifetime). + * - `"request_timeout"` – a single HTTP / RPC request exceeded its deadline. + * - `"execution_timeout"` – a long-running operation (process, watch, etc.) exceeded its allowed duration. + */ +export type TimeoutType = + | 'sandbox_timeout' + | 'request_timeout' + | 'execution_timeout' + // This is the message for the sandbox timeout error when the response code is 502/Unavailable export function formatSandboxTimeoutError(message: string) { return new TimeoutError( - `${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.` + `${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.`, + undefined, + 'sandbox_timeout' ) } @@ -30,11 +44,29 @@ export class SandboxError extends Error { * The [deadline_exceeded] error type is caused by exceeding the timeout for command execution, watch, etc. * * The [unknown] error type is sometimes caused by the sandbox timeout when the request is not processed correctly. + * + * Use the {@link timeoutType} property to determine which kind of timeout occurred + * without having to parse the error message. */ export class TimeoutError extends SandboxError { - constructor(message: string, stackTrace?: string) { + /** + * Indicates which kind of timeout occurred. + * + * - `"sandbox_timeout"` – the sandbox itself timed out (idle / max lifetime). + * - `"request_timeout"` – a single HTTP / RPC request exceeded its deadline. + * - `"execution_timeout"` – a long-running operation (process, watch, etc.) exceeded its allowed duration. + * - `undefined` – the timeout type could not be determined. + */ + readonly timeoutType?: TimeoutType + + constructor( + message: string, + stackTrace?: string, + timeoutType?: TimeoutType + ) { super(message, stackTrace) this.name = 'TimeoutError' + this.timeoutType = timeoutType } } diff --git a/packages/js-sdk/src/index.ts b/packages/js-sdk/src/index.ts index 17d4a97527..d2a1e103a6 100644 --- a/packages/js-sdk/src/index.ts +++ b/packages/js-sdk/src/index.ts @@ -17,6 +17,7 @@ export { BuildError, FileUploadError, } from './errors' +export type { TimeoutType } from './errors' export type { Logger } from './logs' export { getSignature } from './sandbox/signature' diff --git a/packages/python-sdk/e2b/envd/rpc.py b/packages/python-sdk/e2b/envd/rpc.py index f8b2f3b9f8..5963dab597 100644 --- a/packages/python-sdk/e2b/envd/rpc.py +++ b/packages/python-sdk/e2b/envd/rpc.py @@ -33,11 +33,13 @@ def handle_rpc_exception(e: Exception): ) elif e.status == Code.canceled: return TimeoutException( - 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." + 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.", + timeout_type="request_timeout", ) elif e.status == Code.deadline_exceeded: return TimeoutException( - 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." + 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.", + timeout_type="execution_timeout", ) else: return SandboxException(f"{e.status}: {e.message}") @@ -58,4 +60,4 @@ def authentication_header( encoded = base64.b64encode(value.encode("utf-8")).decode("utf-8") - return {"Authorization": f"Basic {encoded}"} + return {"Authorization": f"Basic {encoded}"} \ No newline at end of file diff --git a/packages/python-sdk/e2b/exceptions.py b/packages/python-sdk/e2b/exceptions.py index 75f5f6abf0..be83909706 100644 --- a/packages/python-sdk/e2b/exceptions.py +++ b/packages/python-sdk/e2b/exceptions.py @@ -1,18 +1,21 @@ def format_sandbox_timeout_exception(message: str): return TimeoutException( - 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." + 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.", + timeout_type="sandbox_timeout", ) def format_request_timeout_error() -> Exception: return TimeoutException( "Request timed out — the 'request_timeout' option can be used to increase this timeout", + timeout_type="request_timeout", ) def format_execution_timeout_error() -> Exception: return TimeoutException( "Execution timed out — the 'timeout' option can be used to increase this timeout", + timeout_type="execution_timeout", ) @@ -34,9 +37,17 @@ class TimeoutException(SandboxException): The `canceled` exception type is caused by exceeding request timeout.\n The `deadline_exceeded` exception type is caused by exceeding the timeout for process, watch, etc.\n The `unknown` exception type is sometimes caused by the sandbox timeout when the request is not processed correctly.\n + + The ``timeout_type`` attribute indicates which kind of timeout occurred: + + - ``"sandbox_timeout"`` – the sandbox itself timed out (idle / max lifetime). + - ``"request_timeout"`` – a single HTTP / RPC request exceeded its deadline. + - ``"execution_timeout"`` – a long-running operation (process, watch, …) exceeded its allowed duration. """ - pass + def __init__(self, message: str = "", timeout_type: str | None = None): + super().__init__(message) + self.timeout_type = timeout_type class InvalidArgumentException(SandboxException): @@ -108,4 +119,4 @@ class BuildException(Exception): class FileUploadException(BuildException): """ Raised when the file upload fails. - """ + """ \ No newline at end of file