Repository navigation
sdk: follow a sandbox that moved to another node - #483
Merged
Merged
Conversation
…ntil its lease ends A handle kept the owner it was claimed on, so after a live move, an evacuation or the old node's retirement every call answered 404 or failed to dial until the lease ran out; only Close looked the claim up. Both SDKs now run Lookup once on a 404 or a refused dial from the owner and adopt a different owner, dropping parked connections to the old one. A call whose request is its whole input is sent again there: the control calls and the one-reply and streaming verbs in Go, and every call in Python, whose inputs are bytes. Calls that already streamed input (Go Run with Stdin, WriteFile, Push) or handed back a stream fail once, and the next call reaches the new owner. The MCP server and the LangChain and OpenAI adapters inherit it.
…s to the node that answered Codex review. rebind judged errors by type, so a caller's own stdout writer failing with ECONNREFUSED, or a Python output callback raising a 404 from another sandbox call, made the SDK look the claim up and run the command again on another node. The owner's refused dial, its agent-upgrade 404 and its control-call 404 are now tagged where they arise, and only a tagged error rebinds. Fork, Checkpoint and Promote bound their new handles to the handle's current owner, which a concurrent rebind could have moved off the node that answered; they now take the address that answered.
Codex review round 2. An output callback or writer that itself called another sandbox could surface that call's tagged 404 or refused dial, and the running command would accept it and replay on its own new owner. The tag now names the sandbox id and the owner address it was raised for, and a handle rebinds only on its own tag at its current owner.
… callback, and let a call that lost a race to a move retry at the owner found Codex review round 3. A Python output callback that used the same handle's find_iter could surface a 404 tagged for this very sandbox and owner, and the running command replayed on the new owner. Errors from the caller's code are now cut off where it is called: Go wraps a Run writer's or a ReadFileTo/Pull sink's error in callerError, Python clears the tag of an SDK error an output callback raises, and rebind ignores both. A tagged call whose old owner a concurrent call has already replaced retries at the new owner without a second Lookup.
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.
A handle kept the owner it was claimed on. After a live move (#445), an evacuation (#437) or the old node's retirement, every call answered 404 or failed to dial until the lease ran out; only
Closelooked the claim up. The MCP server and the LangChain and OpenAI adapters hold handles for a long time, so they broke until restart. This was left open by the 2026-11-06 audit (#481).What changes
Both SDKs follow a moved sandbox:
Lookup; a different owner then replaces the old one and parked connections are drained. A tag whose address a concurrent call has already replaced retries at the current owner without a secondLookup.RunwithoutStdin.find_iteris the one exception.RunwithStdin,WriteFile,Push,OpenPty,Watch,DialPort,Lsp.Request,LogsandAttach.Runstdout/stderr,ReadFileTo,Pull) or output callback (Python) is returned as it is, whatever tag it carries.Fork,CheckpointandPromotebind to the node that answered, not to a concurrently rebound owner.Numbers
4 commits. Production +366 −145 (net +221: Go +~110, Python +~70, and the rest moved or re-indented code); tests +245; docs +11.
Gates
make go-lint:0 issues.×10 (5 modules × linux/darwin).make go-testgreen; asl clean on both GOOS.find_iterwas accepted.