Skip to content

Commit 716e0eb

Browse files
nalekseev-e2broman-e2b
authored andcommitted
feat(api): add v2 sandbox creation retry hints
Co-authored-by: roman-e2b <rvanesyan@e2b.dev> GitOrigin-RevId: 5581bb020ac2e135d4a7354bdc9e481893b0afb1
1 parent d5f3532 commit 716e0eb

24 files changed

Lines changed: 1338 additions & 493 deletions

‎docs/ARCHITECTURE.md‎

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -681,14 +681,14 @@ retry errors do not replace the owner's cached result.
681681
##### Response storage and delivery
682682

683683
The executor captures the handler's status and body, then attempts to persist them before
684-
delivery. Redis stores one JSON value containing the status, base64-encoded body, and allowlisted
685-
headers. Base64 preserves arbitrary response bytes; the separate serialized buffer lets Redis
686-
finish a write after the HTTP capture buffer has been released. The cache includes handler errors
687-
as well as successful responses, keeping retries attached to one attempt without inferring which
688-
errors permit safe re-execution.
684+
delivery. Redis stores one JSON value containing the status, base64-encoded body, allowlisted
685+
headers, and optional retryability value. Base64 preserves arbitrary response bytes; the separate serialized
686+
buffer lets Redis finish a write after the HTTP capture buffer has been released. The cache includes
687+
handler errors as well as successful responses, keeping retries attached to one attempt without
688+
inferring which errors permit safe re-execution.
689689

690690
The handler edits response headers in place while the executor buffers status and body writes.
691-
Only `Content-Type`, when present, enters the cache. Replay applies that header over the current
691+
`Content-Type`, when present, enters the cache. Replay applies that header over the current
692692
request's headers, preserving fresh request IDs, rate limits, and cross-origin resource sharing
693693
(CORS) values. The capture pool reuses allocations up to 64 KiB and erases body contents before
694694
reuse because responses can contain credentials.
@@ -718,6 +718,50 @@ a new owner even if the first operation is still running. Redis eviction or data
718718
key, and feature bypass also permit another execution. Redis capacity and access controls must
719719
account for cached response volume and the sandbox credentials those responses can contain.
720720

721+
#### Sandbox creation retry hints
722+
723+
`POST /v2/sandboxes` answers errors with `Should-Retry`, exposed through CORS. The header states
724+
whether the condition behind the error may clear, so that retrying the request could succeed:
725+
`true` for a transient failure such as missing capacity, a rate limit, or a creation still in
726+
progress (retry after `Retry-After` when present, otherwise with exponential backoff); `false`
727+
for a failure a retry cannot fix, such as a malformed request or a configuration fault, and for
728+
an outcome that is unknown, where a retry could create a second sandbox. The header says nothing
729+
about idempotency keys. The executor records every handler result under the key, success or
730+
error, and replays it for as long as the record lives; the caller decides whether a retry keeps
731+
the key or takes a new one, and the error code names the situation that decision depends on.
732+
733+
Each error carries its classification in `APIError.Retryable`, a `Retryability` enum, and the
734+
error writer (`httperror.Write`, behind `SendAPIError`) sets the header from it: `CanRetry` and
735+
`NoRetry` map to `true` and `false`. `UndefinedRetry` (the zero value) leaves the header unchanged:
736+
an absent header stays absent, and a value set earlier is preserved. Callers that discard a
737+
response must explicitly remove its retry hint.
738+
The v2 handler, the rate limiter on that route, and the idempotency executor classify their
739+
errors. Other creation routes share the creation path, so they reset `Retryable` to
740+
`UndefinedRetry` before sending. Successful responses omit the header, and so do admission
741+
errors that carry no classification. A recovered panic removes any hint the handler wrote,
742+
because the hint described a response the panic replaced. Idempotency stores the header with
743+
the cached response and replays it, including its absence.
744+
745+
| Response | Should-Retry |
746+
|---|---|
747+
| Successful creation or replay | absent |
748+
| Authentication/schema/parameter validation errors (including 400, 408, and 5xx), blocked-team rejection, oversized body, or recovered panic | absent |
749+
| Handler input validation, permissions, or missing template/tag | `false` |
750+
| Idempotency key rejected by the middleware's own check, or idempotency configuration fault (retention flag unreadable or not positive, no request deadline) | `false` |
751+
| Request rate limit, with or without a key | `true` |
752+
| Idempotency reservation/lookup failure, or original request still in progress (`idempotency_in_progress`) | `true` |
753+
| Idempotency request mismatch, or unknown outcome of the original request (`idempotency_outcome_unknown`) | `false` |
754+
| Cancellation after acquiring a new idempotency reservation but before execution | `true` |
755+
| Template/volume lookup failure, token generation failure, concurrency reservation error/limit, or unavailable cluster information | `true` |
756+
| Invalid stored build versions or unsupported orchestrator capability | `false` |
757+
| No available/compatible node, only capacity refusals, or placement timeout before any non-refused create attempt | `true` |
758+
| Failed/uncertain create RPC, timeout after a create attempt, or sandbox-store write failure | `false` |
759+
760+
Placement retains uncertainty from earlier attempts even when the last node reports a permanent
761+
error. A sandbox-store write error follows a successful create RPC and only starts asynchronous
762+
cleanup, so a retry could run alongside the sandbox being removed. Classification is not based
763+
on HTTP status alone.
764+
721765
### Sandbox traffic
722766

723767
```mermaid

0 commit comments

Comments
 (0)