diff --git a/.changeset/fix-command-handle-sandbox-exception.md b/.changeset/fix-command-handle-sandbox-exception.md new file mode 100644 index 0000000000..3034e916d0 --- /dev/null +++ b/.changeset/fix-command-handle-sandbox-exception.md @@ -0,0 +1,5 @@ +--- +"e2b": patch +--- + +Raise `SandboxException` instead of bare `Exception` in `CommandHandle.wait()` when the command stream ends without an exit event, giving a clearer error message when the sandbox was killed, paused, or timed out mid-stream. diff --git a/packages/js-sdk/src/sandbox/commands/commandHandle.ts b/packages/js-sdk/src/sandbox/commands/commandHandle.ts index 759d17fe51..78e1f0560b 100644 --- a/packages/js-sdk/src/sandbox/commands/commandHandle.ts +++ b/packages/js-sdk/src/sandbox/commands/commandHandle.ts @@ -162,6 +162,8 @@ export class CommandHandle * If the command exits with a non-zero exit code, it throws a `CommandExitError`. * * @returns `CommandResult` result of command execution. + * @throws {CommandExitError} the command exited with a non-zero exit code + * @throws {SandboxError} the command stream ended before an exit event was received, e.g. the sandbox was killed or timed out */ async wait() { await this._wait @@ -171,7 +173,7 @@ export class CommandHandle } if (!this.result) { - throw new SandboxError('Process exited without a result') + throw new SandboxError('Command stream ended without an exit event. The sandbox is no longer running — it may have been killed, paused, or timed out.') } if (this.result.exitCode !== 0) { diff --git a/packages/python-sdk/e2b/sandbox_async/commands/command_handle.py b/packages/python-sdk/e2b/sandbox_async/commands/command_handle.py index 4168cfe63d..9cb5937d6b 100644 --- a/packages/python-sdk/e2b/sandbox_async/commands/command_handle.py +++ b/packages/python-sdk/e2b/sandbox_async/commands/command_handle.py @@ -244,13 +244,17 @@ async def wait(self) -> CommandResult: If the command exits with a non-zero exit code, it throws a `CommandExitException`. :return: `CommandResult` result of command execution + :raises CommandExitException: the command exited with a non-zero exit code + :raises SandboxException: the command stream ended before an exit event was received, e.g. the sandbox was killed or timed out """ await self._wait if self._iteration_exception: raise self._iteration_exception if self._result is None: - raise Exception("Command ended without an end event") + raise SandboxException( + "Command stream ended without an exit event. The sandbox may have been killed, paused, or timed out." + ) if self._result.exit_code != 0: raise CommandExitException( diff --git a/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py b/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py index ea2b3fdad7..ef134db43e 100644 --- a/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py +++ b/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py @@ -172,6 +172,8 @@ def wait( :param on_stderr: Callback for stderr output :return: `CommandResult` result of command execution + :raises CommandExitException: the command exited with a non-zero exit code + :raises SandboxException: the command stream ended before an exit event was received, e.g. the sandbox was killed or timed out """ try: for stdout, stderr, pty in self: @@ -193,7 +195,9 @@ def wait( raise self._iteration_exception if self._result is None: - raise Exception("Command ended without an end event") + raise SandboxException( + "Command stream ended without an exit event. The sandbox may have been killed, paused, or timed out." + ) if self._result.exit_code != 0: raise CommandExitException(