From 6ca3017714701b1e03d53a1e8766a66a46672a93 Mon Sep 17 00:00:00 2001 From: Tao Lin Date: Fri, 4 Sep 2026 12:09:37 -0400 Subject: [PATCH 1/2] fix(python-sdk): don't crash the build error path on a non-numeric reason.step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_build_step_index hard-codes that reason.step is "base", "finalize", or a numeric string, and int(step) has no fallback — any other value raises ValueError from inside the error path, masking the real BuildException. Such values occur in practice: e2b Cloud's own optimize and resize-disk phases fill step with those literal strings, and E2B-compatible backends have shipped instruction text there (the spec leaves the field an unconstrained string). A step with no usable index now maps to None and the build error is raised without a local traceback, matching the JS SDK's behavior. Also bounds-check negative indices at both call sites. Co-Authored-By: Claude Fable 5 --- packages/python-sdk/e2b/template/utils.py | 10 ++++++--- .../e2b/template_async/build_api.py | 2 +- .../python-sdk/e2b/template_sync/build_api.py | 2 +- .../python-sdk/tests/test_build_step_index.py | 21 +++++++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 packages/python-sdk/tests/test_build_step_index.py diff --git a/packages/python-sdk/e2b/template/utils.py b/packages/python-sdk/e2b/template/utils.py index b61101b307..45979cda1d 100644 --- a/packages/python-sdk/e2b/template/utils.py +++ b/packages/python-sdk/e2b/template/utils.py @@ -406,7 +406,7 @@ def pad_octal(mode: int) -> str: return f"{mode:04o}" -def get_build_step_index(step: str, stack_traces_length: int) -> int: +def get_build_step_index(step: str, stack_traces_length: int) -> Optional[int]: """ Get the array index for a build step based on its name. @@ -414,11 +414,12 @@ def get_build_step_index(step: str, stack_traces_length: int) -> int: - BASE_STEP_NAME: Returns 0 (first step) - FINALIZE_STEP_NAME: Returns the last index - Numeric strings: Converted to number + - Anything else: None (no matching local stack trace) :param step: Build step name or number as string :param stack_traces_length: Total number of stack traces (used for FINALIZE_STEP_NAME) - :return: Index for the build step + :return: Index for the build step, or None if the step has no usable index """ if step == BASE_STEP_NAME: return 0 @@ -426,7 +427,10 @@ def get_build_step_index(step: str, stack_traces_length: int) -> int: if step == FINALIZE_STEP_NAME: return stack_traces_length - 1 - return int(step) + try: + return int(step) + except ValueError: + return None def read_gcp_service_account_json( diff --git a/packages/python-sdk/e2b/template_async/build_api.py b/packages/python-sdk/e2b/template_async/build_api.py index 9a822cd516..3b9445f482 100644 --- a/packages/python-sdk/e2b/template_async/build_api.py +++ b/packages/python-sdk/e2b/template_async/build_api.py @@ -299,7 +299,7 @@ async def poll_status() -> TemplateBuildStatusResponse: step_index = get_build_step_index( build_status.reason.step, len(stack_traces) ) - if step_index < len(stack_traces): + if step_index is not None and 0 <= step_index < len(stack_traces): traceback = stack_traces[step_index] raise BuildException( diff --git a/packages/python-sdk/e2b/template_sync/build_api.py b/packages/python-sdk/e2b/template_sync/build_api.py index 2bc918ab2f..4d5d7bd656 100644 --- a/packages/python-sdk/e2b/template_sync/build_api.py +++ b/packages/python-sdk/e2b/template_sync/build_api.py @@ -289,7 +289,7 @@ def poll_status() -> TemplateBuildStatusResponse: step_index = get_build_step_index( build_status.reason.step, len(stack_traces) ) - if step_index < len(stack_traces): + if step_index is not None and 0 <= step_index < len(stack_traces): traceback = stack_traces[step_index] raise BuildException( diff --git a/packages/python-sdk/tests/test_build_step_index.py b/packages/python-sdk/tests/test_build_step_index.py new file mode 100644 index 0000000000..926084947a --- /dev/null +++ b/packages/python-sdk/tests/test_build_step_index.py @@ -0,0 +1,21 @@ +from e2b.template.consts import BASE_STEP_NAME, FINALIZE_STEP_NAME +from e2b.template.utils import get_build_step_index + + +def test_base_maps_to_first(): + assert get_build_step_index(BASE_STEP_NAME, 5) == 0 + + +def test_finalize_maps_to_last(): + assert get_build_step_index(FINALIZE_STEP_NAME, 5) == 4 + + +def test_numeric_string_maps_to_index(): + assert get_build_step_index("3", 5) == 3 + + +def test_non_numeric_step_returns_none(): + # e2b Cloud itself reports "optimize" / "resize-disk" for those phases. + # None means "no matching local stack trace", not an error. + assert get_build_step_index("optimize", 5) is None + assert get_build_step_index("RUN apt-get update", 5) is None From 9f88c45b662f9e0eec60830f1f15d863cfacb831 Mon Sep 17 00:00:00 2001 From: Tao Lin Date: Fri, 4 Sep 2026 12:34:29 -0400 Subject: [PATCH 2/2] chore: add changeset Co-Authored-By: Claude Fable 5 --- .changeset/tolerate-non-numeric-step.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tolerate-non-numeric-step.md diff --git a/.changeset/tolerate-non-numeric-step.md b/.changeset/tolerate-non-numeric-step.md new file mode 100644 index 0000000000..80ee3ccb96 --- /dev/null +++ b/.changeset/tolerate-non-numeric-step.md @@ -0,0 +1,5 @@ +--- +"@e2b/python-sdk": patch +--- + +fix(python-sdk): don't crash the build error path on a non-numeric reason.step