Skip to content

Vercel session sandbox resume 410 snapshot_not_found does not create a replacement #2373

Description

@blicksky

Version: eve 0.25.2 (production, Vercel backend). Checked current main (packages/eve/src/execution/sandbox/bindings/vercel.ts, vercel-lookup.ts, packages/eve/src/public/channels/github/defaults.ts) — the same resume/lookup and swallowed-checkout behavior is still present.

What happens

A GitHub-channel session is resumed after the Vercel sandbox snapshot has expired (we keep one snapshot per session with a 7-day TTL; Eve sessions last 30 days by default). getSandbox() still returns a handle. The first I/O (Sandbox.get with resume=true) fails:

HTTP 410 Gone
{"error":{"code":"snapshot_not_found","message":"Cannot resume sandbox: no snapshot available."}}

The turn does not fail. GitHub checkout logs GitHub checkout failed — swallowed and the model keeps going. Tool calls (bash, then later readTextFile / removePath in a message.completed handler) all 410 the same way.

Docs (docs/sandbox.mdx) say: if a persisted Vercel sandbox is no longer available, eve creates a replacement from the current template. That path appears to cover 404 (named sandbox missing), not 410 (named sandbox still exists, snapshot gone).

Root cause

getNamedVercelSandbox looks up with resume: false and treats only 404 as missing (vercel-lookup.ts). ensureSession then returns { created: false, sandbox: existing } whenever lookup succeeds.

A snapshot-expired persistent sandbox still has a name, so lookup succeeds. Later I/O resumes it (resume=true) and Vercel returns 410 snapshot_not_found. That 410 is classified as a template-stale error on the create path (isVercelSnapshotUnavailableError), not as “session sandbox gone, create a replacement.”

Compounding: checkoutRepositoryForTurn in github/defaults.ts catches every checkout error, logs GitHub checkout failed — swallowed, and lets the turn continue against a dead VM.

Impact

Any long-lived GitHub conversation (PR review, etc.) whose session outlives its sandbox snapshot fails on the next webhook. The model burns tokens against a workspace it cannot read; channel message.completed handlers that collect sandbox files also throw. Pushing another commit on the same PR resumes the same session and 410s again until the Eve session itself expires.

Observed in production: carrot-pr-agent reviewing carrotfertility/carrot-app#9101. Session created 2026-08-06, last successful sandbox activity 2026-08-11, resumed 2026-08-19 (~8 days idle) → 410 snapshot_not_found on wrun_41KZCQMCFS0GVK7928AGWABK5Q.

Related but different: #1554 (send() drops headSha on resume). We already work around that by nulling checkout state at end of turn. This issue is “there is no filesystem to check out into.”

Reproduction

  1. githubChannel with onPullRequest dispatching synchronize on an existing PR session.
  2. Configure Vercel keepLastSnapshots: { count: 1, expiration: 7 days } (or wait for the default 30-day snapshot TTL while the Eve session is still alive).
  3. Let the snapshot expire. Leave the Eve session in place.
  4. Push a new commit. turn.started checkout 410s (swallowed); tool I/O 410s; message.completed sandbox reads 410.

Suggested fix directions

  • Treat 410 snapshot_not_found on session lookup/resume the same as 404: drop the stale named sandbox and create a replacement from the current template (docs already describe this for “no longer available”).
  • Do not swallow GitHub checkout failures that are sandbox-fatal (410/404). Fail the turn (or recreate, then checkout) instead of running the model against a dead workspace.

Workaround we're using

Channel handler: classify 410 / snapshot_not_found on completion I/O as sandbox-unavailable instead of a generic completion throw, and don’t let findings cleanup throw on the same 410. That improves the failure label; it does not recover the review. A later commit on the same session still 410s.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions