# How ego-browser Handles CDP Session Management and Automatic Re-attachment After Session Loss

> Learn how ego-browser manages CDP sessions and automatically re-attaches after loss. Discover its resilient caching and retry logic for seamless Chrome DevTools Protocol integration.

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

---

**ego-browser maintains a resilient Chrome DevTools Protocol (CDP) layer by caching sessions for 2-second TTLs and automatically re-attaching to targets when sessions are lost using regex-based error detection and transparent retry logic.**

The `ego-browser` package from the [citrolabs/ego-lite](https://github.com/citrolabs/ego-lite) repository provides a robust abstraction over Chrome's CDP that eliminates manual session handling. Unlike raw CDP clients that fail when tabs navigate or crash, ego-browser implements an intelligent recovery mechanism that transparently manages session lifecycles, allowing automation scripts to run without interruption.

## Session Lifecycle and Caching Strategy

ego-browser optimizes CDP connectivity by maintaining a single session per active tab with a short-lived cache to prevent redundant attachment operations.

### Session TTL Configuration

In [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts), the **session freshness window** is defined by the constant:

```typescript
SESSION_TTL_MS = 2000  // Lines 5-6

```

This 2-second TTL ensures that cached sessions remain valid for rapid successive calls while expiring quickly enough to detect underlying target changes. When any CDP method is invoked, the system checks this timestamp; if expired, `ensureSession()` automatically refreshes the connection.

### Active Tab Selection Logic

The `ensureSession()` function (lines 107-143) orchestrates session creation through a deterministic selection process:

1. **Enumerate targets** via `ego.listTabs()`
2. **Select target** using either a user-defined preferred target or the currently active tab
3. **Attach** via `Target.attachToTarget` (using the raw CDP transport) to obtain a unique `sessionId`
4. **Initialize** page-level events by calling `Page.enable`

This sequence ensures that every session is explicitly bound to a specific, verified target before any automation commands execute.

## Automatic Re-attachment Mechanics

When underlying Chrome targets close, crash, or navigate, ego-browser detects the failure and recovers without throwing errors to the caller.

### Detecting Session Loss

The system identifies broken sessions using a precise regex pattern defined at lines 9-10 in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts):

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

```

This pattern catches three distinct failure modes: explicit session ID errors, target closure notifications, and null session states.

### The Recovery Pipeline

The `browserCdp()` function (lines 79-105) implements the core resilience logic:

- **Error interception**: When a CDP request fails, the error message is tested against `SESSION_LOST`
- **Validation check**: The system confirms the request was not an explicit browser-level call (which should not trigger re-attachment)
- **Session invalidation**: Calls `invalidateSession()` (lines 46-54) to clear the cached `sessionId` and `sessionAt` timestamp
- **Transparent retry**: Invokes `ensureSession()` to establish a fresh connection, then retries the original CDP method with the new session

This retry loop is completely transparent to user-level code; callers receive the successful response as if the session never failed.

### Handling Target Detachment Events

The `handleMessage()` listener (lines 52-65) monitors browser-level events including `Target.detachedFromTarget` and `Target.targetDestroyed`. When these events reference the currently stored `sessionTargetId`, the function immediately invokes `invalidateSession()`, forcing the next CDP call to re-attach. This proactive invalidation prevents stale session usage before the TTL expires.

## Key Source Files and Architecture

ego-browser's session management is distributed across three specialized modules:

| File | Role |
|------|------|
| [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) | Contains `browserCdp()`, `ensureSession()`, `invalidateSession()`, and the `SESSION_LOST` detection regex. Implements the automatic recovery loop. |
| [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts) | Holds mutable runtime state including `sessionId`, `sessionAt`, and `sessionTargetId`. Provides the low-level `send` wrapper used by the runtime. |
| [`package/ego-browser/src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/cdp-eval.ts) | Exports the public `cdp()` helper that agents consume. This function forwards to `browserCdp()` and therefore inherits all automatic session management capabilities. |

## Practical Usage Examples

These patterns demonstrate how to leverage ego-browser's resilient CDP layer in automation scripts.

### Basic CDP Calls With Automatic Recovery

The exported `cdp()` helper handles session validation and re-attachment automatically:

```typescript
import { cdp } from "ego-browser";

// This call automatically ensures a valid session, re-attaches if the 
// target was lost, and retries the evaluation.
const result = await cdp("Runtime.evaluate", {
  expression: "document.title",
});
console.log("Page title:", result.result.value);

```

### Manual Session Invalidation

For scenarios requiring explicit control (such as after programmatically reloading a tab), you can force a session refresh:

```typescript
import { invalidateSession, ensureSession } from "ego-browser/src/browser-runtime.js";

// Clear the cached session to force re-attachment on next use
invalidateSession();

// Explicitly create a new session (optional - will happen automatically)
const freshSessionId = await ensureSession();

```

### Subscribing to Browser Events

Monitor CDP events at the browser level while maintaining the auto-recovery benefits:

```typescript
import { subscribeBrowserEvent } from "ego-browser/src/browser-runtime.js";

const unsub = subscribeBrowserEvent(
  "Target.detachedFromTarget",
  undefined, // No session filter - receive all detach events
  (event) => console.log("Target detached:", event.params?.targetId)
);

// Cleanup when done
unsub();

```

## Summary

- **ego-browser caches CDP sessions for 2 seconds** (`SESSION_TTL_MS = 2000`) to balance performance with freshness, as defined in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts).
- **Session loss is detected via regex matching** against `/Session (?:with given id )?not found|Target closed|No session/i`, covering navigation, crashes, and explicit closures.
- **Automatic recovery happens transparently** in `browserCdp()`: invalidating the stale session, calling `ensureSession()` to re-attach, and retrying the original request without exposing errors to callers.
- **Proactive invalidation** occurs when `handleMessage()` receives `Target.detachedFromTarget` or `Target.targetDestroyed` events for the current target.
- **Users control target selection** via `setPreferredTarget()` and `clearPreferredTarget()`, influencing which tab receives the re-attached session.

## Frequently Asked Questions

### How long does ego-browser cache a CDP session before requiring re-attachment?

ego-browser considers a session valid for **2 seconds** (2000ms), defined by the `SESSION_TTL_MS` constant in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts). After this window expires, the next CDP call triggers `ensureSession()` to validate or refresh the connection.

### What happens if the Chrome target crashes while ego-browser is executing a command?

The `browserCdp()` function catches the failure, matches the error against the `SESSION_LOST` regex, invalidates the cached session via `invalidateSession()`, and transparently retries the command after establishing a new session. This recovery occurs within the same async call, so the caller receives the successful result without handling the crash explicitly.

### Can I force ego-browser to attach to a specific tab instead of the active one?

Yes. Use the `setPreferredTarget()` method before invoking CDP commands. When `ensureSession()` runs, it checks for a preferred target ID and attaches to that specific tab rather than querying for the currently active tab. Call `clearPreferredTarget()` to return to default behavior.

### Where does ego-browser store the current session state?

Session state—including `sessionId`, `sessionAt` (timestamp), and `sessionTargetId`—is maintained in [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts). This module also provides the low-level `send` wrapper that [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) uses to communicate with the Chrome DevTools Protocol.