diff --git a/packages/js-sdk/src/envd/rpc.ts b/packages/js-sdk/src/envd/rpc.ts index 9ad34ec6e8..e0e609b1d9 100644 --- a/packages/js-sdk/src/envd/rpc.ts +++ b/packages/js-sdk/src/envd/rpc.ts @@ -10,6 +10,7 @@ import { NotFoundError, SandboxError, TimeoutError, + TimeoutType, } from '../errors' import { ENVD_DEFAULT_USER } from './versions' @@ -26,11 +27,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, + TimeoutType.REQUEST ) 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, + TimeoutType.EXECUTION ) 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..35438e018a 100644 --- a/packages/js-sdk/src/errors.ts +++ b/packages/js-sdk/src/errors.ts @@ -1,7 +1,31 @@ +/** + * The type of timeout that occurred. + * + * - `sandbox` — the sandbox itself timed out (e.g., idle timeout expired). + * - `request` — the HTTP request timed out (exceeded `requestTimeoutMs`). + * - `execution` — a long-running operation timed out (exceeded `timeoutMs` for command execution, watch, etc.). + */ +export enum TimeoutType { + /** + * The sandbox itself timed out (e.g., idle timeout expired). + */ + SANDBOX = 'sandbox', + /** + * The HTTP request timed out (exceeded `requestTimeoutMs`). + */ + REQUEST = 'request', + /** + * A long-running operation timed out (exceeded `timeoutMs` for command execution, watch, etc.). + */ + EXECUTION = 'execution', +} + // 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, + TimeoutType.SANDBOX ) } @@ -23,18 +47,26 @@ export class SandboxError extends Error { /** * Thrown when a timeout error occurs. * - * The [unavailable] error type is caused by sandbox timeout. + * The {@link type} property indicates the kind of timeout: * - * The [canceled] error type is caused by exceeding request timeout. - * - * 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. + * - {@link TimeoutType.SANDBOX} — the sandbox itself timed out (idle timeout, etc.). + * - {@link TimeoutType.REQUEST} — the HTTP request exceeded `requestTimeoutMs`. + * - {@link TimeoutType.EXECUTION} — a long-running operation exceeded its `timeoutMs`. */ export class TimeoutError extends SandboxError { - constructor(message: string, stackTrace?: string) { + /** + * The type of timeout that occurred. + */ + readonly type: TimeoutType + + constructor( + message: string, + stackTrace?: string, + type: TimeoutType = TimeoutType.SANDBOX + ) { super(message, stackTrace) this.name = 'TimeoutError' + this.type = type } } diff --git a/packages/js-sdk/src/index.ts b/packages/js-sdk/src/index.ts index 17d4a97527..16d1269949 100644 --- a/packages/js-sdk/src/index.ts +++ b/packages/js-sdk/src/index.ts @@ -13,6 +13,7 @@ export { SandboxError, TemplateError, TimeoutError, + TimeoutType, RateLimitError, BuildError, FileUploadError, diff --git a/packages/python-sdk/e2b/__init__.py b/packages/python-sdk/e2b/__init__.py index 264fd5deee..12099f5293 100644 --- a/packages/python-sdk/e2b/__init__.py +++ b/packages/python-sdk/e2b/__init__.py @@ -45,6 +45,7 @@ SandboxException, TemplateException, TimeoutException, + TimeoutType, ) from .sandbox.commands.command_handle import ( CommandExitException, @@ -121,6 +122,7 @@ # Exceptions "SandboxException", "TimeoutException", + "TimeoutType", "NotFoundException", "AuthenticationException", "GitAuthException", diff --git a/packages/python-sdk/e2b/envd/rpc.py b/packages/python-sdk/e2b/envd/rpc.py index f8b2f3b9f8..3eecb4e068 100644 --- a/packages/python-sdk/e2b/envd/rpc.py +++ b/packages/python-sdk/e2b/envd/rpc.py @@ -9,6 +9,7 @@ InvalidArgumentException, NotFoundException, TimeoutException, + TimeoutType, format_sandbox_timeout_exception, AuthenticationException, RateLimitException, @@ -33,11 +34,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.", + type=TimeoutType.REQUEST, ) 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.", + type=TimeoutType.EXECUTION, ) else: return SandboxException(f"{e.status}: {e.message}") diff --git a/packages/python-sdk/e2b/exceptions.py b/packages/python-sdk/e2b/exceptions.py index 75f5f6abf0..963a866f61 100644 --- a/packages/python-sdk/e2b/exceptions.py +++ b/packages/python-sdk/e2b/exceptions.py @@ -1,18 +1,38 @@ +from enum import Enum + + +class TimeoutType(str, Enum): + """ + The type of timeout that occurred. + + - ``SANDBOX``: The sandbox itself timed out (e.g., idle timeout expired). + - ``REQUEST``: The HTTP request timed out (exceeded ``request_timeout``). + - ``EXECUTION``: A long-running operation timed out (exceeded ``timeout`` for command execution, watch, etc.). + """ + + SANDBOX = "sandbox" + REQUEST = "request" + EXECUTION = "execution" + + 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.", + type=TimeoutType.SANDBOX, ) def format_request_timeout_error() -> Exception: return TimeoutException( "Request timed out — the 'request_timeout' option can be used to increase this timeout", + type=TimeoutType.REQUEST, ) def format_execution_timeout_error() -> Exception: return TimeoutException( "Execution timed out — the 'timeout' option can be used to increase this timeout", + type=TimeoutType.EXECUTION, ) @@ -30,13 +50,16 @@ class TimeoutException(SandboxException): """ Raised when a timeout occurs. - The `unavailable` exception type is caused by sandbox timeout.\n - 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 ``type`` attribute indicates the kind of timeout: + + - :attr:`TimeoutType.SANDBOX` — the sandbox itself timed out (idle timeout, etc.). + - :attr:`TimeoutType.REQUEST` — the HTTP request exceeded ``request_timeout``. + - :attr:`TimeoutType.EXECUTION` — a long-running operation exceeded its ``timeout``. """ - pass + def __init__(self, message: str = "Timeout", type: TimeoutType = TimeoutType.SANDBOX): + super().__init__(message) + self.type = type class InvalidArgumentException(SandboxException):