What Happens When an Action Is Interrupted

BrowserSkill treats interruption as a reconciliation problem, not as a simple process exit. This chapter moves from familiar symptoms—“session busy,” a timeout, a user interrupt, a lost WebSocket, or a daemon that disappears with a sandboxed shell—to the mechanisms that explain them. The goal is to diagnose what is queued, what may have reached the browser, what cleanup is still running, and why a missing response must not automatically be interpreted as no effect.

From a busy session to an unknown effect

A BrowserSkill session has one per-session dispatch queue. The daemon forwards one tool RPC to the owning extension and waits for its response before taking the next ordinary job. That serialization can look conservative when several calls appear independent, but the session contains shared state: the current reference store, debugger attachments, target claims, hover state, and browser operations that may change the page for the next call. If two operations race, the second could use a reference while the first is changing the document, or both could manipulate the same debugger target in conflicting ways.

The queue therefore protects a causal sequence, not just a thread. A navigation followed by an observation should not be reordered. A borrow or return should not overlap a session stop. A click that changes the page should not race another operation that assumes the previous observation is still current. When a second unfinished command arrives, BrowserSkill can return a session-busy result rather than pretending that concurrency is harmless. The caller may wait, cancel, or inspect the existing operation, but it is not invited to create a second uncontrolled timeline inside the same session.

This serialization is a trade-off rather than a claim that all browser work must be globally sequential. Different sessions can have separate queues and can operate in parallel when their browser ownership is distinct. The restriction is local to the state that can race: one session's references, debugger attachments, borrowed-tab claims, and page transitions. That boundary preserves useful concurrency across tasks while avoiding the more dangerous assumption that two calls using the same session are independent merely because their command names differ.

The queue also explains why cancellation may take time. A queued request can be marked cancelled before it reaches the extension, in which case the worker can stop it without sending a browser operation. A forwarded request is different. The daemon may need to send a cancel frame, the extension may need to trip an AbortController, and the handler may need to finish compensation: release an object, restore an overlay, close a partially created tab, return a borrowed tab, or discard a capture. The original caller can be told that cancellation was requested while the session remains busy until the browser-side cleanup is resolved or a bounded cleanup deadline is reached.

This ordering is especially important for browser-native input. A click, key press, or wheel event can be partially dispatched. If cancellation arrives before the input is sent, the result can be a straightforward cancellation. If the event may already have reached Chrome, the system cannot honestly say that nothing happened.