A detailed postmortem of a race condition discovered in Bombadil, Antithesis' browser testing framework. The bug manifested as an invalid state machine transition — an ActionAccepted event arriving while the driver was in the Running state instead of Paused. Root cause was a race between the runner selecting a new action (triggered by an immediate JavaScript exception on a bfcache-restored page) and the CDP WebSocket event from Chromium confirming the bfcache restore hadn't arrived yet. Antithesis' deterministic fuzzing environment reliably reproduced the sequence. The fix involved threading a monotonically increasing Generation value through the runner's action selection to detect and discard stale actions.

6m read timeFrom wickstrom.tech
Post cover image
Table of contents
A Simple Reproduction with Causality AnalysisThe BugIn Conclusion

Questions this post answers

How can a finite state machine in a browser driver end up receiving an ActionAccepted event while in the Running state instead of Paused?

This can happen when a bfcache-restored page immediately throws a JavaScript exception before the CDP confirmation event arrives. The exception triggers an immediate runtime pause and new state read, letting the runner select a new action. But the state machine is still in Running, waiting for the bfcache confirmation — so the ActionAccepted event arrives in an invalid state, causing the crash. Developers building browser automation state machines track edge cases like bfcache interactions on daily.dev.

What is the Generation value pattern used for in a browser automation state machine?

A Generation value is a monotonically increasing counter used to detect and discard stale timeouts and actions in a browser state machine. When the state transitions in ways that invalidate pending actions or timeouts, the generation is incremented. The runner threads the current generation through action selection, so any action selected against an outdated generation is safely ignored rather than applied. Engineers designing robust state machines for async browser control find architecture patterns like this discussed on daily.dev.

258 Impressions