Reclaim Modal sandboxes whose create call was interrupted - #2406
Open
nicolaslara wants to merge 1 commit into
Open
Reclaim Modal sandboxes whose create call was interrupted#2406nicolaslara wants to merge 1 commit into
nicolaslara wants to merge 1 commit into
Conversation
Modal commits a sandbox and hands it to the scheduler before `Sandbox.create` returns, so a create interrupted in flight still boots and bills a sandbox — measured at roughly a second after the caller stopped waiting for it. `_sandbox` is only assigned once the call returns, so an interrupted `start` leaves nothing for `teardown` or the atexit backstop to act on, and the sandbox runs to its 24h maximum lifetime. Cancellation is the common trigger: Ctrl-C on an eval, or a rollout cancelled while its batch is still provisioning. `CancelledError` is not an `Exception`, so it also slipped past the existing handler and left no log line. The name is ours and unique per rollout, so an interrupted create is still addressable. Look it up on the way out and adopt it, and the owner's `stop` disposes of it like any other sandbox. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
Interrupt an eval that uses the Modal runtime while sandboxes are still being provisioned, and more sandboxes stay alive than the harness ever reports as up. They then run until their 24h
timeoutexpires.Cause
Modal commits the sandbox and hands it to the scheduler before
Sandbox.createreturns. A create interrupted in flight therefore still produces a running, billed sandbox. Timeline measured on one such sandbox:In
ModalRuntime.startthe handle is only assigned once the call returns:So
_sandboxstaysNone, and both cleanup paths key off exactly that attribute —teardownreturns early, and thecleanupatexit backstop no-ops. The sandbox is unreachable from the process that made it.Cancellation is the common trigger (Ctrl-C, or a rollout cancelled while its batch is still provisioning), and
CancelledErroris not anException, so it also slipped pastexcept Exceptioninstartand left no log line. That is why the "sandbox ... up" count doesn't match what's actually running. A connection dropped on the reply has the same shape.Fix
The
namepassed toSandbox.createis ours and unique per rollout, and Modal enforces name uniqueness per app, so an interrupted create is still addressable. Look it up on the way out and adopt it; the owner'sstopthen disposes of it like any other sandbox._adopt_orphanis a no-op when a handle is already held, so it is also safe on the existingexcept Exceptionpath — which additionally covers a create that failed after the server created the sandbox.Verification
Real Modal sandboxes, 12 rollouts started concurrently, batch cancelled mid-create, then every runtime stopped exactly as the owner's
finallydoes.LEAKEDis measured withSandbox.listagainst the app, not inferred:Before
After
In run 1 after the fix, one rollout's create never reached the server (
created_server_side=11), so there was correctly nothing to adopt.Cancelling outside the create window is unaffected: the handle is already held and
_adopt_orphanreturns immediately.uv run ruff check,uv run ruff format --check, anduv run ty check verifiersall pass. No tests added, perAGENTS.md— the check above was a temporary script.Two notes for maintainers, not changed here
ModalConfig.creates_per_secdefaults to 40.0. Modal's default per-workspace sandbox creation limit is 5/s with a 150-token burst bucket, so this default is 8× over for a workspace that hasn't had its limit raised. Creates then retry with backoff, which keeps them in flight much longer and widens the window this PR closes. Worth either lowering the default or documenting that the limit needs raising.Sandbox.createacceptsidle_timeout. It is the only cleanup that survives losing the client entirely — a hard kill, or a second Ctrl-C that kills the event loop before any recovery code runs. Currently onlytimeout=24his set, so anything the process can't clean up lives for a day.Disclosure: I work at Modal. This came out of debugging a leak with a user running verifiers on us.
🤖 Generated with Claude Code