# How Session Export to HTML Handles Deep Tree Traversal in Pi Web

> Discover how Pi Web's session export to HTML prevents stack overflow crashes using an iterative patch for deep tree traversal. Learn about the loop-based approach.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: internals
- Published: 2026-08-14

---

**Pi Web's session export uses an iterative post-processing patch to replace recursive tree rendering with a loop-based approach, preventing stack overflow crashes on deeply nested conversations.**

The **pi-web** repository implements a robust server-side export mechanism that converts chat sessions into self-contained HTML files. When users request an export, the system must handle conversation trees of arbitrary depth—from a few messages to thousands of linear branches—without crashing the browser's JavaScript engine.

## The Export Pipeline Architecture

Session export in Pi Web follows a three-stage pipeline designed for reliability at scale.

### 1. Session Retrieval via API Route

The export flow begins at `app/api/sessions/[id]/export/route.ts`. This API handler coordinates the entire conversion process:

```typescript
// GET /api/sessions/[id]/export
// Located in: app/api/sessions/[id]/export/route.ts

```

The handler first retrieves the session data using [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts), which parses the `.jsonl` file format that Pi Web uses for persistent storage. Each session is stored as newline-delimited JSON entries representing individual messages with parent-child relationships.

### 2. SDK HTML Generation

The handler delegates initial HTML production to Pi's internal SDK via `exportSessionToHtml`. This generates a complete HTML document including:

- Embedded CSS for message styling
- The raw message tree as a JavaScript data structure
- A `renderTreeRecursive` function for DOM construction

The recursive renderer follows a natural but fragile pattern:

```javascript
// Original SDK output (recursive, vulnerable to stack overflow)
function renderTreeRecursive(node, depth = 0) {
  renderMessage(node, depth);
  node.children.forEach(child => renderTreeRecursive(child, depth + 1));
}

```

For sessions with 1000+ messages in a linear chain, this exceeds typical browser call-stack limits (~10,000–50,000 frames depending on engine), causing the exported page to crash on load.

### 3. The Iterative Patch

Pi Web's critical innovation occurs immediately after SDK generation. The export route **post-processes the HTML string** to replace the vulnerable recursive implementation:

```javascript
// Patched version inserted by Pi Web's export handler
function renderTreeIterative(root) {
  const stack = [{ node: root, depth: 0 }];
  while (stack.length) {
    const { node, depth } = stack.pop();
    // render node …
    for (let i = node.children.length - 1; i >= 0; i--) {
      stack.push({ node: node.children[i], depth: depth + 1 });
    }
  }
}

```

This transformation maintains identical rendering behavior while keeping call-stack depth constant at **O(1)** regardless of tree depth. The stack array grows in heap memory—safely handling millions of nodes.

## Client-Side Export Trigger

Users initiate exports through a simple fetch pattern:

```tsx
// In a React component
const exportSession = async (sessionId: string) => {
  const res = await fetch(`/api/sessions/${sessionId}/export`);
  const blob = await res.blob();
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `session-${sessionId}.html`;
  a.click();
};

```

The server returns the fully patched HTML as a downloadable blob—no additional client-side processing required.

## Key Implementation Files

| File Path | Responsibility |
|-----------|---------------|
| `app/api/sessions/[id]/export/route.ts` | API endpoint, orchestrates export and applies iterative patch |
| [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) | Parses `.jsonl` session files into traversable objects |
| [`lib/export-html.ts`](https://github.com/agegr/pi-web/blob/main/lib/export-html.ts) | Internal SDK that generates initial HTML (recursive) |

## Why Iterative Beats Recursive for Session Export

**Recursion risks:** Browser JavaScript engines impose strict call-stack limits. Deep conversation chains—common in extended research or coding assistant sessions—trigger `RangeError: Maximum call stack size exceeded`.

**Iteration guarantees:** The explicit stack pattern uses heap-allocated arrays with no engine-imposed depth limits. Memory consumption scales linearly with tree size, which is acceptable for practical session lengths.

The patch is applied **server-side** so exported files remain functional independently—critical for archives and shared downloads.

## Summary

- Pi Web stores sessions as `.jsonl` files parsed by [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts)
- The export route in `app/api/sessions/[id]/export/route.ts` generates HTML via SDK then patches it server-side
- The iterative `renderTreeIterative` replacement eliminates stack overflow risks for deep conversation trees
- Exported HTML files are self-contained and work across all browsers regardless of session depth

## Frequently Asked Questions

### What file format does Pi Web use for session storage?

Pi Web uses **JSON Lines (`.jsonl`)** format, with each line representing one message entry containing content, timestamps, and parent references. This append-only structure supports efficient streaming reads during export.

### Why not fix the recursion in the SDK directly?

The SDK's `exportSessionToHtml` is internal to Pi's broader platform. Pi Web applies the iterative patch as a **consumer-side safeguard**, ensuring compatibility without requiring upstream changes. This defensive approach also permits SDK updates without regression risk.

### Does the iterative version change visual output?

No. The `renderTreeIterative` function produces **identical DOM structures** to the original recursive implementation. The transformation is purely algorithmic—swapping the engine's call stack for an explicit array stack while preserving traversal order.

### How deep can sessions be after the patch?

The iterative approach supports **millions of messages** limited only by available memory. The explicit stack array grows in heap space, bypassing browser call-stack limits entirely.