Skip to content

Commit 3f46d56

Browse files
mishushakovclaude
andauthored
fix(sdk): select stack-trace frames by SDK boundary instead of fixed depth (#1599)
## Description Template build stack traces were captured by walking a fixed number of frames (`STACK_TRACE_DEPTH` plus `±1` arithmetic at ~15 call sites), which broke whenever the frame count between `new Error()` and user code shifted — TS class-field initializer frames (#1539) and Bun's tail-call frame elision were both this bug. This PR makes two related changes: 1. **Boundary-based frame selection.** The caller's frame is now the first one whose file lies outside the SDK package, making extra transpiler frames and elided delegating frames irrelevant. In the JS SDK, frame parsing is delegated to `error-stack-parser-es` (ESM-only, so it's a devDependency inlined into both dist formats via tsdown `noExternal` — the engines range includes Node versions without `require(esm)`); the Python SDK equivalently walks `f_back` until `co_filename` leaves the `e2b` package root, in the shared builder used by both sync and async. If no user frame is identifiable (e.g. the SDK is bundled into the caller's own file), capture degrades to no trace rather than a wrong frame. 2. **Dead machinery removed.** Because boundary capture resolves through SDK-internal delegation (`remove()` → `runCmd()`, `fromDockerfile()` → parser) to the user's call site on its own, the suppress/override collection machinery (`runInNewStackTraceContext`, `runInStackTraceOverrideContext`, the enabled/override flags, and their Python equivalents) became redundant and is removed — superseding the approach in #1596. Error `.stack` synthesis (keeping the `Name: message` header and the throw site on `cause`) was prototyped here and backed out — it will come as a follow-up PR. ## Usage No API changes — build errors now point at the user's call site regardless of runtime or transpiler: ```ts const template = Template() .fromBaseImage() .runCmd('./does-not-exist') // ← build failures point exactly here await Template.build(template, 'my-template') ``` ## Testing - JS: `unit` + `template` vitest projects green against the real API (incl. 27 per-method stacktrace tests pinning exact call-site line/columns, `bunInstall` now covered); edge-compat bundle test and CLI build verified; built CJS/ESM dists smoke-tested with `require()`/`import()`. - Python: all 184 template tests green (shared + sync + async, incl. both `test_stacktrace.py` suites, `bun_install` now covered); `ruff` and `ty` clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 00253c3 commit 3f46d56

15 files changed

Lines changed: 289 additions & 393 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'e2b': patch
3+
'@e2b/python-sdk': patch
4+
---
5+
6+
Select template build-step stack-trace frames by SDK boundary instead of fixed depth. The caller's frame is now the first one whose file lies outside the SDK package, so traces stay correct when transpilers inject extra frames (e.g. TS class-field initializers) or runtimes elide delegating frames (e.g. Bun's tail-call elision). The suppress/override stack-trace collection machinery this made redundant (`runInNewStackTraceContext`, `runInStackTraceOverrideContext` and their Python equivalents) is removed.

packages/js-sdk/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@vitest/browser": "^4.1.10",
5757
"@vitest/browser-playwright": "^4.1.10",
5858
"dotenv": "^16.4.5",
59+
"error-stack-parser-es": "^2.0.1",
5960
"json-schema-to-typescript": "^15.0.4",
6061
"knip": "^5.43.6",
6162
"msw": "^2.12.10",

packages/js-sdk/src/template/consts.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,6 @@ export const FINALIZE_STEP_NAME = 'finalize'
1212
*/
1313
export const BASE_STEP_NAME = 'base'
1414

15-
/**
16-
* Stack trace depth for capturing caller information.
17-
*
18-
* Depth levels:
19-
* 1. Template function
20-
* 2. TemplateBase class
21-
* 3. Caller method (e.g., copy(), fromImage(), etc.)
22-
*
23-
* This depth is used to determine the original caller's location
24-
* for stack traces.
25-
* @internal
26-
*/
27-
export const STACK_TRACE_DEPTH = 3
28-
2915
/**
3016
* Default setting for whether to resolve symbolic links when copying files.
3117
* When false, symlinks are copied as symlinks rather than following them.

0 commit comments

Comments
 (0)