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

> Ego-Lite automatically handles CDP session loss by detecting errors, recovering stale state, and re-attaching to browser targets without manual intervention. Learn how it works.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-08-23

---

**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`](https://github.com/citrolabs/ego-lite/blob/main/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.

- **Source:** `ensureSession` in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) (lines 107–139)

## 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:

```typescript
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.

- **Source:** `browserCdp` error handling in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) (lines 94–103)

## 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.

- **Source:** `invalidateSession` in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) (lines 46–54)

## 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.

- **Source:** `browserCdp` retry logic in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) (lines 99–102)

## 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.

- **Sources:** `handleMessage` handling of detach events (lines 52–66) and `enablePageEvents` (lines 105–115) in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts)

## Practical Code Examples

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

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

// Automatically obtains a fresh session if the previous one vanished
await cdp('Network.enable');

```

```typescript
// 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

```

```typescript
// 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.