Repository navigation
Terminate the VM a gateway deploy created when the deploy fails or is cancelled - #141
Conversation
… cancelled A VM-path gateway deploy that failed or was cancelled after create_vm left the VM running, or on the local provider its compose stack: no record named it yet, and close() ends only container sandboxes. Each attempt now terminates the VM it created when it ends that way, as does a cancellation during the tools probe, but never the caller's existing sandbox or one an earlier deploy made. A chained provider's failed members no longer each leave theirs. A cancelled deploy is closed like a failed one, and an env's close() lets a cancellation through, keeping its sandbox for the next close(). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| created, self._sandbox = self._sandbox, before | ||
| if created is None or created is before or created is kept or created in self._container_sandboxes: | ||
| return | ||
| try: | ||
| await created.terminate() | ||
| except Exception as e: | ||
| logger.warning(f"Failed to terminate {type(created).__name__} {created.sandbox_id} after a failed deploy: {e}") |
There was a problem hiding this comment.
Failed cleanup cannot be retried
If terminate() fails, _discard_vm logs the error after removing the only saved VM handle. For example, LocalSandbox.terminate() raises when docker compose down fails. Provider close() does not retry VM termination, so the caller must remove the leftover stack or VM by hand.
As a non-blocking improvement, keep failed terminations in a pending-cleanup list that close() retries, and add a test for this path.
Knowledge Base Used: Provider integrations and sandbox deployment
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/providers/env_providers/env_gateway_provider.py
Line: 623-629
Comment:
**Failed cleanup cannot be retried**
If `terminate()` fails, `_discard_vm` logs the error after removing the only saved VM handle. For example, `LocalSandbox.terminate()` raises when `docker compose down` fails. Provider `close()` does not retry VM termination, so the caller must remove the leftover stack or VM by hand.
As a non-blocking improvement, keep failed terminations in a pending-cleanup list that `close()` retries, and add a test for this path.
**Knowledge Base Used:** [Provider integrations and sandbox deployment](https://app.greptile.com/scale-ai/-/custom-context/knowledge-base/scaleapi/agentenv-framework/-/docs/provider-integrations.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Not taken: close() runs moments after the failed terminate, so a retry there would most likely hit the same error (Docker unreachable, compose down failing). The warning names the sandbox, so it can be removed by hand.
There was a problem hiding this comment.
That’s fair. A retry from the immediately following close() would likely encounter the same provider/Docker failure, while adding durable pending-cleanup state and retry policy would expand this change beyond the deploy-cancellation fix. The warning includes the sandbox type and ID, so operators still have an actionable recovery path. I agree this is a non-blocking operational improvement and does not need to be addressed in this PR.
…er and the caller's sandboxes by name Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rovider's concern Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A gateway deploy on a VM-mode provider creates its VM first, then loads images, starts the compose stack and waits for the gateway and its card. If anything after
create_vmfailed, or the deploy was cancelled (for example Ctrl-C during "Starting MCP services…"), the VM kept running:close()ends only container-mode sandboxes;env._sandboxis set only afterdeploy()returns.On the
localprovider that "VM" is a compose stack on your machine, so it held ports and memory until removed by hand. A chained provider (beta_scale,modal_vm) also left one VM behind for each member that failed before another succeeded.Before / after, measured on
localwith real Docker:Createdlocal,local, first member failsexisting_sandboxHow
_deploy_gateway: when the VM path raises, including on cancellation, the attempt terminates the VM it created (_terminate_new_sandbox). It never terminates the caller'sexisting_sandbox, one an earlier deploy made, or a container sandbox;close()already ends those. Because this happens per attempt, each failed chained member cleans up after itself._launch: a cancellation during the tools probe, after a successful attempt, does the same. The probe itself never raises._run: closes onBaseException, so a cancelled deploy releases its containers and state like a failed one.MCPServerEnv/WebsiteEnv/MultiEnvclose(): catchExceptionrather thanBaseExceptionaround terminating the sandbox. A cancellation now propagates instead of being swallowed, and the sandbox is kept for the nextclose().Verified
A live driver ran on
localwith real Docker, using its own registry, stores and sandbox root. The faults are real: a cancel while the stack comes up, and a gateway that never becomes ready (injected at_wait_for_gateway, so it fails at once rather than after 300 s). The five scenarios in the table pass on this branch. The first two leak as shown on unmodifiedmain.Unit tests pass. New ones cover:
close()keeping its sandbox.Not covered here
deploy()on the same env object that fails can still tear down the first deployment. Only direct Python callers do that, since task steps always load a fresh env.~/.agent-env-sandboxes. The stack is gone.🤖 Generated with Claude Code

Confidence Score: 5/5
The PR appears safe to merge; the earlier suggestion to retry failed VM cleanup remains non-blocking.Fix with agent prompt
Summary
Failed or cancelled gateway deployments now terminate the VM they created. Caller-owned sandboxes are left alone.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Start gateway attempt] --> B[Create VM] B --> C[Load images and start services] C --> D{Attempt succeeds?} D -->|No or cancelled| E[Terminate newly created VM] E --> F[Close containers and state] F --> G[Try next provider or raise] D -->|Yes| H[Probe tools] H --> I{Probe cancelled?} I -->|Yes| E I -->|No| J[Return deployment]Reviews (3) · Last reviewed commit: "Call it _terminate_new_sandbox: what kin..." · Reviewed by Greptile