# How to Debug Stub Memory Leaks in Long-Lived Sessions Using CAPNWEB_TRACK_STUBS

> Debug stub memory leaks in long-lived sessions with CAPNWEB_TRACK_STUBS. Enable tracking, then use stubSnapshot() or GET /__computerd/stubs to find and fix leaks.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: how-to-guide
- Published: 2026-08-08

---

**Set `CAPNWEB_TRACK_STUBS=1` to enable weak-reference tracking of `RpcTarget` stubs in Cloudflare Computer, then call `stubSnapshot()` or query `GET /__computerd/stubs` to detect leaks by verifying all per-class counters reach zero after disposal.**

Cloudflare Computer uses **Cap'n Proto-Web (capnweb)** for the RPC channel between Durable Objects and the `computerd` daemon, where every cross-boundary object becomes an `RpcTarget` stub that must be explicitly disposed to prevent memory exhaustion in long-lived isolates. When `CAPNWEB_TRACK_STUBS` is enabled, the runtime maintains diagnostic counters that track stub lifecycles via `trackStub()` and `untrackStub()` without impacting production performance. This guide explains how to activate this subsystem, capture snapshots, and integrate leak detection into your soak tests.

## Understanding Stub Memory Leaks in Cap'n Proto-Web

In [`packages/rpc/src/debug.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/debug.ts), Cloudflare Computer implements a **diagnostic subsystem** that monitors `RpcTarget` instances crossing the RPC boundary. Each stub represents a remote object that should receive `[Symbol.dispose]` when the session ends or the stub is no longer needed.

In long-lived sessions—such as persistent Workers or containers that continuously fetch new stubs—a missed disposal manifests as a **stub memory leak**. The stub object persists in the RPC layer even after the remote side discards it, slowly consuming heap space. The tracking mechanism uses a `WeakSet` to hold live instances and a `Map` to count objects per class name, ensuring zero production overhead when disabled. As noted in the source, *"The point is measurement, not enforcement: snapshot() returns the per-class live count so a soak script can assert 'after a quiet point, every counter is zero.' Anything non-zero is a leak…"* ([`debug.ts#L1-L10`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/debug.ts#L1-L10)).

## Enabling the CAPNWEB_TRACK_STUBS Diagnostic Mode

You can activate stub diagnostics via environment variable or programmatic API. Set `CAPNWEB_TRACK_STUBS=1` before starting the process, or import `enableStubTracking()` from `@cloudflare/computer-rpc/debug`:

```typescript
import { enableStubTracking } from "@cloudflare/computer-rpc/debug";

// Explicitly enable if environment variable is not set
enableStubTracking();

```

When enabled, every `RpcTarget`-derived class automatically calls `trackStub(this)` in its constructor. When `[Symbol.dispose]` executes, `untrackStub(this)` decrements the corresponding counter. The implementation is deliberately a no-op when tracking is off, ensuring production workloads incur no penalty.

## Capturing Stub Snapshots to Detect Leaks

The diagnostic API exposes `stubSnapshot()`, which returns a plain object mapping class names to live instance counts. A zero value across all keys indicates healthy disposal; non-zero values reveal specific leak locations.

**Programmatic inspection:**

```typescript
import { stubSnapshot } from "@cloudflare/computer-rpc/debug";

const leaks = stubSnapshot();
// Example output: { DebugTrackerTarget: 2 }
console.log("Live stubs:", leaks);

```

**HTTP endpoint inspection:**

When running the `computerd` daemon with tracking enabled, [`packages/computerd/src/cli/computerd.ts`](https://github.com/cloudflare/computer/blob/main/packages/computerd/src/cli/computerd.ts) registers `GET /__computerd/stubs` to expose the same snapshot. According to the documentation, you can *"read `stubSnapshot()` from `@cloudflare/computer-rpc/debug`, or hit `GET /__computerd/stubs` on a computerd instance"* ([`README.md#L391-L393`](https://github.com/cloudflare/computer/blob/main/packages/computer/README.md#L391-L393)):

```bash
export CAPNWEB_TRACK_STUBS=1
curl http://localhost:8787/__computerd/stubs

# => {"DebugTrackerTarget":0}

```

## Soak Testing Workflow for Leak Detection

Integrate stub tracking into long-running tests to catch regressions early. The recommended workflow separates measurement from enforcement, allowing you to assert zero leaks after quiescence:

1. **Enable tracking** using `CAPNWEB_TRACK_STUBS=1` or `enableStubTracking()`.
2. **Exercise the RPC layer** through your workload (e.g., repeated `workspace.runtime.exec` calls).
3. **Pause execution** to allow pending disposals to settle.
4. **Capture a snapshot** via `stubSnapshot()` or the HTTP endpoint.
5. **Assert zero counts** for all stub classes; non-zero values indicate the specific `RpcTarget` implementation with broken disposal contracts.

## Complete Debugging Example

The following TypeScript example demonstrates a complete leak detection pattern, including settling time and assertion based on the source test patterns:

```typescript
import {
  enableStubTracking,
  stubSnapshot,
  isStubTrackingEnabled,
} from "@cloudflare/computer-rpc/debug";

// Enable diagnostics
enableStubTracking();

// Simulate RPC-heavy workload
for (let i = 0; i < 100; i++) {
  const stub = await workspace.runtime.exec("echo hi");
  // Stub should auto-dispose when RPC completes
}

// Allow disposal callbacks to settle
await new Promise((r) => setTimeout(r, 200));

// Inspect for leaks
const snapshot = stubSnapshot();
if (Object.keys(snapshot).length > 0) {
  throw new Error(`Stub leak detected: ${JSON.stringify(snapshot)}`);
}

```

For daemon-level monitoring, query the administrative endpoint:

```bash
curl -s http://localhost:8787/__computerd/stubs | jq

```

## Key Source Files and Implementation Details

Understanding the source architecture helps interpret leak reports:

- **[`packages/rpc/src/debug.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/debug.ts)**: Core implementation containing `trackStub()`, `untrackStub()`, and `stubSnapshot()`. Maintains the `WeakSet` of live instances and per-class counters.
- **[`packages/rpc/tests/debug.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/tests/debug.test.ts)**: Unit tests validating counting logic and idempotent disposal behavior.
- **[`packages/computerd/src/cli/computerd.ts`](https://github.com/cloudflare/computer/blob/main/packages/computerd/src/cli/computerd.ts)**: Lines 230-250 implement the `GET /__computerd/stubs` HTTP handler that returns JSON snapshots when tracking is active.
- **[`packages/computer/README.md`](https://github.com/cloudflare/computer/blob/main/packages/computer/README.md)**: Documents the "Stub leak hunting" procedure and environment variable configuration.

## Summary

- **Set `CAPNWEB_TRACK_STUBS=1`** to activate zero-overhead diagnostic tracking of `RpcTarget` stubs in the Cloudflare Computer runtime.
- **Query `stubSnapshot()`** programmatically or access `GET /__computerd/stubs` on the daemon to retrieve per-class live counts.
- **Assert zero counters** after workload quiescence; non-zero values pinpoint specific stub classes with missing `[Symbol.dispose]` implementations.
- **Track leaks in soak tests** by enabling diagnostics, running sustained RPC workloads, and verifying disposal completeness to prevent long-lived session exhaustion.

## Frequently Asked Questions

### What causes stub memory leaks in Cloudflare Computer?

Stub leaks occur when `RpcTarget` objects crossing the Cap'n Proto-Web RPC boundary fail to receive `[Symbol.dispose]` calls. In long-lived isolates that continuously create new stubs, each missed disposal accumulates orphaned objects in the RPC layer, eventually exhausting available heap memory.

### Is CAPNWEB_TRACK_STUBS safe for production environments?

While safe, the tracking adds minimal overhead via `WeakSet` and `Map` operations. The subsystem is designed as a no-op when disabled, but the diagnostic mode is intended primarily for development, testing, and debugging sessions rather than high-traffic production deployments.

### How do I interpret non-zero values in stubSnapshot()?

Non-zero counts in the snapshot object indicate the exact number of live instances for that specific stub class. For example, `{ DebugTrackerTarget: 2 }` means two instances of that class have not been disposed. Check the corresponding class implementation in your codebase for missing disposal calls or broken disposal contracts.

### Can I enable stub tracking without restarting the computerd daemon?

Yes, call `enableStubTracking()` programmatically from within your Worker or application code before creating RPC stubs. However, to access the HTTP endpoint `GET /__computerd/stubs`, the daemon must have been started with `CAPNWEB_TRACK_STUBS=1` or you must restart it with the variable set.