How Ego-Lite Handles CDP Session Loss: Automatic Recovery and Re-Attachment

Ego-Lite automatically detects Chrome DevTools Protocol (CDP) session loss through regex error pattern matching and transparently recovers by invalidating stale state and re-attaching to fresh browser targets without requiring manual intervention.

Ego-Lite, an open-source browser automation library maintained by Citro Labs, implements a session-centric runtime that shields automation scripts from transient browser disconnections. Unlike raw CDP clients that fail when the underlying session vanishes, the library handles CDP session loss through a combination of TTL-based caching, proactive error detection, and automatic retry logic. This resilience is implemented primarily in package/ego-browser/src/browser-runtime.ts, where the transport layer manages the entire session lifecycle.

Session Lifecycle and TTL Management

The runtime maintains a 2-second freshness window for active sessions using the SESSION_TTL_MS = 2000 constant. When a CDP command requires a session, the ensureSession() function checks state.sessionAt to determine if the cached session ID is still valid.

If the timestamp is within the TTL, the cached session is returned immediately. If the session has expired or is missing, ensureSession() attaches to a fresh target and updates the runtime state with the new session ID and current timestamp. This logic prevents unnecessary re-attachment during high-frequency operations while ensuring stale sessions are abandoned promptly.

Detecting CDP Session Loss at Runtime

When a CDP request fails, the runtime analyzes the error in the browserCdp() catch block using a specific regex pattern:

const SESSION_LOST = /Session (?:with given id )?not found|Target closed|No session/i;

If the error message matches this pattern and the request was not explicitly bound to a specific session (indicating a "normal" helper call), the runtime flags the condition as a lost session. This detection occurs before any error is propagated to the caller, allowing the runtime to intercept the failure and initiate recovery.

Cleaning Up Invalid Sessions

Upon detecting a lost session, the runtime immediately calls invalidateSession() to clear the corrupted state. This function removes the cached session ID, clears any pending dialog trackers associated with the stale session, and unsubscribes from page events specific to that session ID. By fully purging the old state before attempting recovery, the runtime prevents race conditions and ensures that subsequent commands operate on a clean slate.

Transparent Re-Attachment and Retry

After invalidation, browserCdp() automatically invokes ensureSession() to acquire a fresh session and re-issues the original CDP request with the new session ID. This retry is completely transparent to the calling code—helpers such as cdp(), js(), and driver-level operations like click() never need to implement their own retry logic. The caller receives the successful response as if the session loss never occurred, maintaining the fluency of automation scripts.

Monitoring Target Lifecycle Events

Beyond request-time error handling, the runtime subscribes to Target domain events to detect session loss proactively. When the browser emits Target.detachedFromTarget or Target.targetDestroyed, the handleMessage() function checks if the affected target matches the current session's target. If there is a match, invalidateSession() is invoked immediately, preventing subsequent commands from attempting to use a dead session.

Additionally, before sending page-specific CDP commands, the runtime calls enablePageEvents(sessionId) to guard against enabling events on an already-lost session.

Practical Code Examples

The following patterns demonstrate how the automatic recovery works in practice:

// Example 1 – Simple CDP call
import { cdp } from 'ego-browser';

// Automatically obtains a fresh session if the previous one vanished
await cdp('Network.enable');
// Example 2 – Evaluating JavaScript
import { js } from 'ego-browser';

const title = await js('document.title');
// Transparently re-attaches and retries if the session was lost mid-call
// Example 3 – Driver-level interactions
import { click } from 'ego-browser/driver/pointer';

await click('button#submit');
// Internally calls browserCdp; session loss is handled automatically

Summary

  • TTL-Based Caching: Sessions are considered valid for 2 seconds (SESSION_TTL_MS = 2000) to minimize re-attachment overhead.
  • Regex Detection: The SESSION_LOST pattern identifies session expiration via error messages like "Session not found" or "Target closed".
  • Automatic Invalidation: The invalidateSession() function clears stale IDs, dialog trackers, and event subscriptions immediately upon detection.
  • Transparent Retry: Failed requests are automatically retried on fresh sessions without exposing transport errors to calling code.
  • Proactive Monitoring: Target lifecycle events trigger immediate session invalidation before commands fail.

Frequently Asked Questions

How does ego-lite detect that a CDP session has been lost?

The runtime detects loss by matching CDP error messages against the SESSION_LOST regex in the browserCdp() error handler. This regex catches strings like "Session with given id not found", "Target closed", or "No session". Additionally, it monitors Target.detachedFromTarget and Target.targetDestroyed events to invalidate sessions when the browser proactively reports disconnection.

What is the purpose of the 2000ms session TTL?

The SESSION_TTL_MS = 2000 constant defines a 2-second freshness window managed by ensureSession(). This TTL prevents excessive re-attachment during rapid command sequences while ensuring that sessions older than 2 seconds are refreshed, balancing performance against the risk of using stale connections.

Do I need to manually handle session recovery when using helpers like cdp() or js()?

No. Helpers such as cdp(), js(), and driver operations rely on browserCdp(), which implements automatic retry logic. When a session is lost, the runtime invalidates the stale state, acquires a new session, and retries the original command transparently. Your code receives the successful result without needing try-catch blocks for transport-level session errors.

What happens when the browser emits Target.detachedFromTarget events?

The runtime's handleMessage() function listens for Target.detachedFromTarget and Target.targetDestroyed events. If the detached target matches the current session's target, invalidateSession() is called immediately to clear the cached session ID. This proactive cleanup prevents subsequent commands from attempting to communicate with a destroyed target.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →