Skip to content

Give sandbox task creation a realistic timeout and diagnosable failures - #332454

Merged
Osvaldo Ortega (osortega) merged 3 commits into
mainfrom
osortega/sandbox-task-create-reliability
Aug 25, 2026
Merged

Give sandbox task creation a realistic timeout and diagnosable failures#332454
Osvaldo Ortega (osortega) merged 3 commits into
mainfrom
osortega/sandbox-task-create-reliability

Conversation

@osortega

@osortega Osvaldo Ortega (osortega) commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Creating a cloud sandbox task ran on the generic 10s request budget, but the call waits on a sandbox environment being provisioned rather than on a record being written. Successful creates routinely take considerably longer than that, so every create was aborted client-side before any response arrived. It surfaced as a network failure, when really we were hanging up while the request was still in flight.

What this does

Task creation gets its own 60s budget. Other calls keep the 10s one.

Failures are now diagnosable. A failed create can come back with a generic message that does not identify the underlying cause, so nothing client-side can explain it on its own. Failed creates now log the response verbatim — status, body, and x-github-request-id, which is what a support escalation is keyed on. A small set of retry-related response headers is captured for the same reason.

Orphaned tasks are cleaned up. A task record can already exist by the time a create fails, so a failure can leave one behind — not just the authorization rejection that was previously handled. When the response names the task, it is deleted; when it does not, the log says so explicitly rather than staying silent about something that can then only be removed server-side.

A rejected cleanup is no longer reported as a successful one. _request resolves non-2xx responses rather than throwing, so a failed DELETE was logged as "Cleaned up" while the orphan was still there. The status now decides, and a rejected delete takes the same warning path as a thrown one.

Two subtleties worth a reviewer's eye

  • asText(context) consumes the stream. Reading it twice silently yields an empty string, which was dropping the message explaining the failure — so the body is read once and threaded through to _throwForStatus.
  • No blind retry on 5xx. It is tempting, but a generic 500 here does not by itself indicate a retryable condition, and the backend signals genuinely retryable failures explicitly — ours carried no such signal. Retrying blind would also multiply the orphaned tasks.

Also fixes a hardcoded GET in the request failure log, and sends Content-Type: application/json for bodied requests, which fetch would otherwise label text/plain.

Validation

  • npm run typecheck-client, eslint on both changed files
  • ./scripts/test.sh --glob "**/cloudSandboxApiService.test.js" — 17 passing

Each new assertion was checked against the bug it is meant to catch, by reverting the production change and confirming the test fails:

Reverted Test that fails
60s create budget posts the on-demand sentinel and returns the bound environment
Content-Type: application/json same
isSuccess check on cleanup reports a rejected cleanup rather than claiming the orphan was removed

The request stub previously discarded timeout and headers, so an earlier revision of this PR would have stayed green with the budget and content type reverted. It no longer does.

Copilot AI balanced review requested due to automatic review settings August 25, 2026 00:08

Copilot AI 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.

Pull request overview

Adds reliable cloud sandbox task creation and failure diagnostics.

Changes:

  • Extends creation timeout to 60 seconds and sends JSON content type.
  • Logs failure details and attempts orphan cleanup.
  • Adds failure and cleanup tests.
Show a summary per file
File Description
cloudSandboxApiService.ts Updates request handling, diagnostics, and cleanup.
cloudSandboxApiService.test.ts Adds failed-create tests and log assertions.

Review details

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Creating a cloud sandbox task ran on the generic 10s request budget, but
Mission Control provisions a sandbox VM before it replies, so the call waits
on a machine being allocated rather than on a record being written. Every
create was aborted client-side before any response arrived, which surfaced as
a network failure. Task creation now gets its own 60s budget.

Failures were also hard to act on. Mission Control writes the task record
before provisioning compute, so a failure can leave a task behind, and the
provisioner answers with a generic `failed to create agent compute` that
masks the upstream cause. Failed creates now log the response verbatim,
including `x-github-request-id`, which is the only handle that correlates
with the owning team's logs. When the response names the task it recorded,
that orphan is deleted; when it does not, the log says so explicitly.

The response body is read once and threaded through to the thrown error:
reading it twice silently yields an empty string and drops the message
explaining the failure.

Also fixes a hardcoded `GET` in the request failure log, and sends
`Content-Type: application/json` for bodied requests, which `fetch` would
otherwise label `text/plain`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A rejected DELETE resolves like any other response rather than throwing, so
the cleanup logged "Cleaned up unusable sandbox task" for an HTTP 500 and the
orphan it claimed to remove was still there. The status now decides, and a
failed cleanup takes the same warning path as a thrown one, saying explicitly
that the task remains and can only be removed server-side.

The existing cleanup test reused the create's 500 for the DELETE too, so it
passed without ever showing a successful removal. The stub now answers the
delete on its own, and a new test covers the rejected case directly.

Assert the creation budget and the JSON content type, which no test covered:
the request stub discarded `timeout` and `headers`, so reverting either
production change left the suite green. The budget is pinned as a literal
rather than compared against its own constant, which would still pass if it
were lowered back to the value that aborted every create.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@osortega
Osvaldo Ortega (osortega) force-pushed the osortega/sandbox-task-create-reliability branch from a04e77f to 93387ec Compare August 25, 2026 00:59
@osortega
Osvaldo Ortega (osortega) marked this pull request as ready for review August 25, 2026 02:52
@osortega
Osvaldo Ortega (osortega) enabled auto-merge (squash) August 25, 2026 02:52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@osortega Osvaldo Ortega (osortega) changed the title Give sandbox task creation a realistic budget and diagnosable failures Give sandbox task creation a realistic timeout and diagnosable failures Aug 25, 2026
@osortega
Osvaldo Ortega (osortega) merged commit 16178b0 into main Aug 25, 2026
27 checks passed
@osortega
Osvaldo Ortega (osortega) deleted the osortega/sandbox-task-create-reliability branch August 25, 2026 03:25
@vs-code-engineering vs-code-engineering Bot added this to the 1.136.0 milestone Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants