Skip to content

fix(sdk): single-source the on_timeout default and make the auto_resume error honest - #2058

Open
AdaAibaby wants to merge 2 commits into
e2b-dev:mainfrom
AdaAibaby:fix/on-timeout-default-contract
Open

AdaAibaby wants to merge 2 commits into
e2b-dev:mainfrom
AdaAibaby:fix/on-timeout-default-contract

Conversation

@AdaAibaby

@AdaAibaby AdaAibaby commented Oct 29, 2026 •

Copy link
Copy Markdown

Problem

Fixes #2057.

Both SDKs hardcode the API's on_timeout / onTimeout default (kill) in two places — doc comments and client-side auto_resume validation — coupling them to an infra-owned default (AutoPauseDefault in e2b-dev/infra) with no single source of truth. Because an unset on_timeout resolves locally to kill, auto_resume: true with no on_timeout is rejected with a message that blames an on_timeout the caller never set.

What this does

  • Single source of truth: add DEFAULT_ON_TIMEOUT ('kill') in both SDKs (sandboxApi.ts, sandbox_api.py), documented as mirroring the API's AutoPauseDefault. The scattered ?? 'kill' / else "kill" literals now reference it, so the assumption lives in one place with a pointer to the owning repo. It is never sent to the API — an omitted on_timeout still leaves auto_pause unset so the server applies its own default — it only resolves local validation.
  • Honest error: when on_timeout is unset, the auto_resume rejection now says it defaulted to kill and that you should set on_timeout to pause, instead of blaming an action the caller didn't choose. An explicit non-pause action keeps a distinct message naming the value.
  • Tests: both SDKs' lifecycle tests assert the clarified message (JS: full suite in lifecycleRequest.test.ts — 24/24 pass; Python: test_create_rejects_auto_resume_without_a_timeout_action asserts the message names the default).

On the options in the issue

  • Option 1 (defer validation to the server) is unsafe: the API does not reject auto_resume paired with a no-pause timeout, so dropping the client-side check would silently accept a no-op config (auto-resume on a sandbox that gets killed on timeout). Kept the client-side rejection.
  • Option 2 (source the default from the OpenAPI spec) isn't available: the spec carries no default for autoPause (only autoPauseMemory defaults true). The constant + documented pointer is the closest single-source we can do without an API-side spec change — if the spec later exposes an autoPause default, the constant is the one spot to wire it to.
  • Option 3 (clarify the message) — done.

The SandboxInfo.on_timeout round-trip asymmetry noted in the issue (can't represent left to API default) is left as a separate follow-up — it needs an API/response-shape change, out of scope here.

Not included

Issue #2056 (whether the default itself should be pause rather than kill) is a product/API default decision for maintainers, not a code change this PR makes.

Test environment note

The JS lifecycle request suite (the one I touched) passes 24/24. Three tests in the separate lifecyclePayload.test.ts fail only with AuthenticationError: API key is required (integration-style, need a real E2B_API_KEY) and are unrelated to this change. Python unit tests couldn't run in my sandbox due to a pre-existing protobuf-codegen gap in the env; the changed module and test compile clean and ruff passes.


Devin Review

…me error honest

Both SDKs hardcoded the API's on_timeout default ('kill') in two places — doc
comments and client-side auto_resume validation — coupling them to an
infra-owned default with no single source. An unset on_timeout resolved locally
to 'kill', so auto_resume=true with no on_timeout was rejected with a message
blaming on_timeout the caller never set.

- Add DEFAULT_ON_TIMEOUT ('kill') in both SDKs: one documented constant that
  mirrors the API's AutoPauseDefault (e2b-dev/infra). It is never sent — an
  omitted on_timeout still leaves auto_pause unset so the server applies its own
  default — it only resolves local validation, now in one place.
- Make the auto_resume rejection honest: when on_timeout is unset, the error
  says it defaulted to 'kill' and to set on_timeout to 'pause', instead of
  blaming an action the caller did not choose.
- Tests assert the clarified message in both SDKs.

Keeps the client-side rejection (not deferring to the server): the API does not
reject auto_resume paired with a no-pause timeout, so dropping the local check
would silently accept a no-op (auto-resume on a sandbox that gets killed).

Refs e2b-dev#2057
@changeset-bot

changeset-bot Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2d930b0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
e2b Patch
@e2b/python-sdk Patch
@e2b/code-interpreter-python Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T04:24:05.761173Z cb1b398 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Devin Review: 1 flag

Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against TASTE.md (e2b-dev/sdk-harness). I checked parity (T-1/T-2), constants and defaults (T-47), entry-point exports (T-54), validation scope (T-52), error class and message shape (T-59, T-62, T-63), and docs (T-69). JS and Python change in step, and both errors stay InvalidArgumentError/InvalidArgumentException.

4 violations, all inline:

  • T-69: the new JS constant sits between the SandboxOnTimeout JSDoc and the type, so the public type has lost its docs.
  • T-54: DEFAULT_ON_TIMEOUT is exported from sandboxApi.ts but not from index.ts, so only a deep import reaches it. It should be module-private.
  • T-52 / T-62 (JS and Python, same issue): the new "not set" message states the server's current default, so it goes stale as soon as infra changes AutoPauseDefault. Say "not set, set it to 'pause'" without naming the default.

Not tied to a changed line:

  • T-52, overall: a constant keeps the assumption in fewer places in each SDK, but the SDK still copies a backend default: there is one copy per SDK, and the onTimeout/on_timeout docs still say "currently kill" separately (sandboxApi.ts SandboxLifecycle.onTimeout; sandbox_api.py SandboxLifecycle and on_timeout docstrings). So the PR's "single source of truth" claim only holds within the validation code. If the default can change, the stronger fix is the API-side spec default mentioned in the PR body.
  • Not a TASTE rule: the JS test went from toThrowError(InvalidArgumentError) to a message regex only, so it no longer checks the T-59 error class. Python still checks it with pytest.raises(InvalidArgumentException). Worth asserting both in JS.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb1b3986a4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…ror, python changeset

Review (Devin TASTE.md + Codex):
- T-69: move DEFAULT_ON_TIMEOUT out from between the SandboxOnTimeout JSDoc and
  its type (the public type had lost its docs); place it after the type.
- T-54: make DEFAULT_ON_TIMEOUT module-private (drop export) — it is not
  re-exported from index.ts and the Python twin isn't in __all__, so this keeps
  parity and avoids publishing an infra-owned default as SDK contract.
- T-52/T-62: drop the server default ('kill') from the auto_resume error. The
  actionable part — set on_timeout to 'pause' — holds whatever the server
  default is; naming 'kill' would go stale if the API default changes (e2b-dev#2056).
  Also fixes mixed quoting in the Python message.
- Test: JS now asserts both the InvalidArgumentError class (T-59) and the
  clarified message, not a message regex alone.
- Codex P1: add '@e2b/python-sdk' to the changeset — the Python exception text
  changed too and releases as a separate target.

Refs e2b-dev#2057
@AdaAibaby

Copy link
Copy Markdown
Author

Thanks for the thorough review — addressed all of it in 2d930b0.

Devin (TASTE.md):

  • T-69 — moved DEFAULT_ON_TIMEOUT out from between the SandboxOnTimeout JSDoc and its type; it now sits after the type, so the public type keeps its docs.
  • T-54 — made the constant module-private (dropped export). It's not re-exported from index.ts and the Python twin isn't in __all__, so this keeps JS/Python parity and avoids publishing an infra-owned default as SDK contract.
  • T-52 / T-62 (JS + Python) — removed the server default ('kill') from the auto_resume error. The actionable part ("you didn't set onTimeout; set it to 'pause'") holds regardless of the server default, so it won't go stale if Sandbox killed (not paused) by default on timeout — silent, irreversible state loss when on_timeout is omitted #2056 flips the default. Also fixed the mixed 'pause'/"pause" quoting in the Python message.
  • Test (T-59) — JS now asserts both the InvalidArgumentError class and the clarified message, not a message regex alone.

Codex P1 — added '@e2b/python-sdk': patch to the changeset; the Python exception text changed too and releases as a separate target.

On the broader T-52 note (the SDK still copies a backend default, and the onTimeout/on_timeout docstrings still say "currently kill"): agreed the stronger fix is an API-side spec default, which the OpenAPI schema doesn't expose today (only autoPauseMemory has one). I left the "currently kill" wording in the user-facing docstrings since it's useful guidance, but the validation now has a single internal source and the error message no longer asserts the default — so nothing user-facing goes false if the default changes. Happy to follow up with an API/spec change to make it a true single source if maintainers want.

Verified: JS lifecycleRequest.test.ts 24/24, tsc/prettier/oxlint clean on the changed file, ruff clean, Python compiles. (The lifecyclePayload.test.ts failures are pre-existing AuthenticationError needing a real API key, unrelated to this change.)

@AdaAibaby

Copy link
Copy Markdown
Author

Companion PR on the infra side: e2b-dev/runtime#3767 adds a contract test asserting the OpenAPI spec's autoPause default matches the Go constant the API applies (sandboxtypes.AutoPauseDefault). That addresses the drift risk from the other direction — this PR single-sources the SDK's local assumption, #3767 keeps the spec (which this SDK documents as the default) in sync with actual server behavior.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDK hardcodes the on_timeout default (currently kill) in docs and validation, with no contract against the API default

2 participants