Skip to content

fix: report fatal sandbox errors to the session and its caller - #1599

Open
duboff wants to merge 1 commit into
ColeMurray:mainfrom
chattermill:fix/notify-caller-on-fatal-sandbox-error
Open

fix: report fatal sandbox errors to the session and its caller#1599
duboff wants to merge 1 commit into
ColeMurray:mainfrom
chattermill:fix/notify-caller-on-fatal-sandbox-error

Conversation

@duboff

@duboff duboff commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

A sandbox that dies before it can run leaves its session sitting active with no messages, and never tells whoever asked for the work. Three things line up to produce that:

  • The event is rejected at the parse layer. supervisor.py already posts {"error": message, "fatal": true} via _report_fatal_error, but the error variant of sandboxEventSchema extended messageSandboxEventBaseSchema, so messageId was required. A fatal boot error happens before any message exists, so the one event that most needs to get through is exactly the one that cannot. fatal was not in the schema at all.
  • Nothing moved session status. Only execution_complete transitions, so the session stays active indefinitely. At any scale this is unpleasant to operate: a total sandbox outage is indistinguishable from a pile of merely-slow sessions.
  • Nothing notified the caller. notifyComplete is only reached from execution_complete, so the prompt stays pending forever and the Slack/Linear/GitHub bot that requested the session hears nothing back. On a PR that means the bot's acknowledgement reaction reads as "still thinking" rather than "died before it began".

Changes

  • shared: on the error event, make messageId optional and add fatal. processSandboxEvent already falls back to the processing message when messageId is absent.
  • message-queue: add failUnfinishedMessages, next to the existing failMessage that already synthesizes the terminal event, records it, projects it and notifies the caller for stop, cancel and the stuck-processing timeout. That path only ever inspects the processing message; a prompt that fails during boot is still pending, which is why it was never covered.
  • sandbox-events: on a fatal error, settle in-flight prompts and transition the session to failed, after persist and broadcast so the reason is on record first.
  • web: an error event can now arrive without a messageId, so it cannot be keyed to a parent Task call — leave it at the top level rather than mis-nesting it.

Notes

  • Settling before a possible late execution_complete is safe: recordMessageCompletion only writes while the row still holds the expected status, so whichever event arrives second finds nothing to settle and delivers no second callback.
  • A non-fatal error stays advisory — status and in-flight prompts are untouched — which the tests pin.

Test plan

  • npm run typecheck and npm run lint clean
  • control-plane 3171 tests pass, including new coverage for settling a pending prompt, settling a processing prompt against its own status, the no-op case, and the non-fatal case leaving everything alone
  • shared 686, slack-bot 420, github-bot 135, linear-bot 224 pass
  • web 1213 pass; the single failure in use-session-rename.test.tsx reproduces on an unmodified main and is unrelated to this change

Summary by CodeRabbit

  • Bug Fixes
    • Fatal sandbox errors now correctly fail in-progress prompts and mark the session as failed.
    • Advisory errors no longer alter active prompts or session status.
    • Fatal startup errors appear correctly as top-level timeline events.
  • Reliability
    • In-flight messages now receive completion notifications when a sandbox cannot continue.
    • Error events can be reported without being associated with a specific message.

The supervisor already posts {error, fatal: true} when a sandbox cannot
continue, but nothing downstream acts on it.

The error event extended messageSandboxEventBaseSchema, so messageId was
required — and a fatal boot error happens before any message exists, so
the event was rejected at the parse layer. fatal was not in the schema at
all. Even parsed, only execution_complete moved session status or called
notifyComplete, so a repository that would not clone or an image that
would not start left the session sitting active with no messages, and
whoever asked for the work heard nothing.

Settling reuses the queue's existing failMessage, which already
synthesizes the terminal event, records it, projects it and notifies the
caller for stop, cancel and the stuck-processing timeout. That path only
ever sees processing; a prompt that fails during boot is still pending.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4cef203b-cd61-4116-b458-2920f8a1bdd3

📥 Commits

Reviewing files that changed from the base of the PR and between 703c341 and eec5dc5.

📒 Files selected for processing (7)
  • packages/control-plane/src/session/durable-object.ts
  • packages/control-plane/src/session/message-queue.test.ts
  • packages/control-plane/src/session/message-queue.ts
  • packages/control-plane/src/session/sandbox-events.test.ts
  • packages/control-plane/src/session/sandbox-events.ts
  • packages/shared/src/types/sandbox-events.ts
  • packages/web/src/lib/timeline-items.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Fatal sandbox errors can now fail unfinished prompts and transition the session to failed. Error events may omit messageId and include fatal. Message-less fatal events remain top-level in the timeline.

Changes

Sandbox error handling

Layer / File(s) Summary
Sandbox error event contract
packages/shared/src/types/sandbox-events.ts, packages/web/src/lib/timeline-items.ts
Error events now support optional messageId and fatal fields. Subtask events without a message ID remain top-level.
Unfinished message failure
packages/control-plane/src/session/message-queue.ts, packages/control-plane/src/session/message-queue.test.ts
The queue fails pending and processing prompts, notifies pending callers, broadcasts updates, and returns the failure count.
Fatal event processing
packages/control-plane/src/session/sandbox-events.ts, packages/control-plane/src/session/durable-object.ts, packages/control-plane/src/session/sandbox-events.test.ts
Fatal errors fail unfinished messages and transition the session to failed after event persistence and broadcasting. Tests cover fatal and non-fatal errors.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to eec5d

This change reports fatal sandbox startup failures to sessions and callers instead of leaving work pending; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: colemurray

Sequence Diagram(s)

sequenceDiagram
  participant Sandbox
  participant SessionSandboxEventProcessor
  participant SessionMessageQueue
  participant statusService
  Sandbox->>SessionSandboxEventProcessor: Send fatal error event
  SessionSandboxEventProcessor->>SessionSandboxEventProcessor: Persist and broadcast event
  SessionSandboxEventProcessor->>SessionMessageQueue: Fail unfinished messages
  SessionSandboxEventProcessor->>statusService: Transition session to failed
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes reporting fatal sandbox errors to the session and caller, which is the main change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant