# How to Debug Runtime Type Issues in Cloudflare Computer: A Step-by-Step Guide

> Debug Cloudflare Computer runtime type issues with RPC debug logging, stub shape validation, and execution-runtime mapping checks. Catch type mismatches early.

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

---

**Enable RPC debug logging with `enableRpcDebug()`, validate stub shapes using `assertRpcImplements()`, and verify execution-runtime mappings with `assertExecutionRuntime()` to catch type mismatches before they propagate.**

Cloudflare Computer separates the **workspace** (the in-process file system) from the **runtime** (the sandbox that executes code). This architecture relies on strongly-typed RPC boundaries that can fail silently when types diverge across process boundaries. Understanding how to debug runtime type issues in Cloudflare Computer requires knowledge of three interconnected layers: the Cap'n Proto wire contract, the runtime implementation, and the type assertion utilities that catch mismatches early.

## Understanding the Runtime Type Architecture

Types flow across three main layers in the Cloudflare Computer stack. When a runtime type error surfaces, the root cause typically lives in one of these boundaries.

| Layer | What It Provides | Key Source Files |
|-------|------------------|------------------|
| **Workspace ↔ Runtime RPC** | Strongly-typed Cap'n Proto interface for `exec`, `pull`, `push`, etc. | [[`runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/runtime/wire.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/wire.ts) |
| **Runtime Implementation** | Concrete classes driving containers or workers, exposing `WorkspaceRuntime` API | [[`runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime/runtime.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts) |
| **Type Helpers & Assertions** | Utility types and runtime-only checks like `assertExecutionRuntime` | [[`runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/runtime/types.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) |

Common sources of runtime type failures include:

- **Mismatched RPC contracts** — The Cap'n Proto definition in [`wire.ts`](https://github.com/cloudflare/computer/blob/main/wire.ts) diverges from TypeScript declarations
- **Incorrect stub casting** — The `RpcStub` proxy in [`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts) casts to nominal types without runtime validation
- **Runtime-only globals** — Missing or differently-shaped globals like `process` or `WebSocket` in worker environments
- **Exec-tracking mismatches** — Stale execution ID mappings between `WorkspaceRuntime` instances

## Step-by-Step Debugging Workflow

### Reproduce the Error in a Test

Write a minimal reproduction in [`packages/computer/tests/runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/tests/runtime.test.ts). The test harness spins up a real container (`computerd`) or worker stub, giving you authentic cross-process behavior.

```typescript
// packages/computer/tests/runtime.test.ts
import { describe, it, expect } from "vitest";
import { createTestWorkspace } from "./helpers";

describe("runtime type issues", () => {
  it("should reproduce the type mismatch", async () => {
    const workspace = await createTestWorkspace();
    // Your reproduction case here
    const result = await workspace.runtime.exec({ /* ... */ });
    // Assertion that fails due to type mismatch
  });
});

```

### Enable Detailed RPC Logging

Import the debug helper from the RPC package to surface exact payloads:

```typescript
import { enableRpcDebug } from "@cloudflare/rpc/debug";

enableRpcDebug();  // prints JSON-encoded messages for each RPC call

```

This reveals the precise field values causing type mismatches across the wire.

### Inspect the Wire Contract

Compare definitions in [`runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/runtime/wire.ts) against [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts). Check for:

- Missing or extra fields in structs
- Incorrect enum values (e.g., `WireErrorCode`)
- Union types not narrowed correctly

The Cap'n Proto schema is the source of truth. When it diverges from TypeScript interfaces, runtime failures follow.

### Validate Stub Casting

In [`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts), `newWebSocketRpcSession` performs a double cast:

```typescript
const stub = new RpcStub(ws, "SyncRPC") as SyncRPC;

```

This compiles successfully even when the remote side implements a different shape. Add a runtime guard:

```typescript
import { assertRpcImplements } from "@cloudflare/rpc/debug";

assertRpcImplements(syncStub, "SyncRPC");
// Throws with field-level diffs if shapes diverge

```

### Check Execution-Runtime Tracking

The `WorkspaceRuntime` class stores execution ID to runtime ID mappings. When `assertExecutionRuntime` fails, you've hit a cross-runtime boundary violation.

In [`runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime/runtime.ts), `#rememberExecutionRuntime` stores the mapping:

```typescript
// Internal implementation detail from runtime.ts
#rememberExecutionRuntime(execId: string, runtimeId: string): void {
  this.#execToRuntime.set(execId, runtimeId);
}

```

Later calls to `assertExecutionRuntime` (from [`runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/runtime/types.ts)) verify consistency:

```typescript
import { assertExecutionRuntime } from "@cloudflare/computer/src/runtime/types";

async function verifyExecutionContext(
  execId: string,
  expectedRuntimeId: string
) {
  const exec = await workspace.runtime.getExec({ id: execId });
  
  // Throws: "expected runtime X but got Y" on mismatch
  assertExecutionRuntime("my-operation", expectedRuntimeId, exec.runtimeId);
  
  // Safe to proceed
  return exec;
}

```

Fixes typically involve:
- Passing correct `runtimeId` to `exec`, `pull`, or `push`
- Ensuring `Workspace` instances aren't reused across runtimes without resetting `#runtime`

### Run Tests with Native Build

Some type checks only exercise when `fuse-native` is built:

```bash
npm run build                          # builds all workspace packages

npm test --workspace @cloudflare/computer

```

Failures appearing only with native builds indicate FUSE shim issues rather than pure-TypeScript problems.

### Use the Debug Wrapper for Ad-Hoc Inspection

The `debugRpc` function wraps any stub with runtime type checking:

```typescript
import { debugRpc } from "@cloudflare/rpc/debug";

const rawStub = await workspace.getRpcStub();
const safeStub = debugRpc(rawStub);  // validates arguments at runtime

await safeStub.pull({ path: "/foo.txt" });  // logs and validates

```

### Fix the Contract

Once identified, update the source of truth:

1. Edit [`runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/runtime/wire.ts) for Cap'n Proto changes
2. Adjust [`interface.ts`](https://github.com/cloudflare/computer/blob/main/interface.ts) definitions to match
3. Regenerate schema: `npm run build` in `packages/rpc`
4. Re-run failing tests

## Code Examples for Common Scenarios

### Enabling RPC Debug Logging

```typescript
import { enableRpcDebug } from "@cloudflare/rpc/debug";

enableRpcDebug();  // JSON line per request/response

```

### Adding Runtime Shape Assertions

```typescript
import { newWebSocketRpcSession } from "capnweb";
import { assertRpcImplements } from "@cloudflare/rpc/debug";

const ws = new WebSocket(url);
const syncStub = newWebSocketRpcSession(ws, "SyncRPC") as SyncRPC;

// Runtime guard against contract drift
assertRpcImplements(syncStub, "SyncRPC");

```

### Verifying Execution-Runtime Consistency

```typescript
import { assertExecutionRuntime } from "@cloudflare/computer/src/runtime/types";

async function doSomething(execId: string, expectedRuntimeId: string) {
  const exec = await workspace.runtime.getExec({ id: execId });
  assertExecutionRuntime("operation", expectedRuntimeId, exec.runtimeId);
  // Proceed with validated execution context
}

```

### Wrapping Stubs with Debug Validation

```typescript
import { debugRpc } from "@cloudflare/rpc/debug";

const safeStub = debugRpc(await workspace.getRpcStub());
await safeStub.pull({ path: "/foo.txt" });  // validated at runtime

```

## Key Source Files Reference

| File | Role | Link |
|------|------|------|
| [`packages/computer/src/runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts) | Core `WorkspaceRuntime`; exec ID tracking, lifecycle management | [[`runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts) |
| [`packages/computer/src/runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) | RPC payload types, exec-runtime assertions, utilities | [[`types.ts`](https://github.com/cloudflare/computer/blob/main/types.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts) |
| [`packages/computer/src/runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/wire.ts) | Cap'n Proto contract between Durable Object and `computerd` | [[`wire.ts`](https://github.com/cloudflare/computer/blob/main/wire.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/wire.ts) |
| [`packages/computer/tests/runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/tests/runtime.test.ts) | Test suite for runtime layer reproductions | [[`runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/runtime.test.ts)](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.test.ts) |
| [`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts) | RPC stub creation over WebSocket | [[`client.ts`](https://github.com/cloudflare/computer/blob/main/client.ts)](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts) |
| [`packages/rpc/src/debug.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/debug.ts) | `enableRpcDebug`, `debugRpc`, `assertRpcImplements` | [[`debug.ts`](https://github.com/cloudflare/computer/blob/main/debug.ts)](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/debug.ts) |

## Summary

- **Cloudflare Computer's type system spans three layers**: Cap'n Proto wire contract, runtime implementation, and assertion utilities
- **`enableRpcDebug()`** surfaces exact RPC payloads causing mismatches
- **`assertRpcImplements()`** catches stub casting errors before runtime failure
- **`assertExecutionRuntime()`** validates execution context hasn't crossed runtime boundaries
- **Native builds** may expose type issues hidden in pure-TypeScript paths
- Always fix contracts at the source: [`wire.ts`](https://github.com/cloudflare/computer/blob/main/wire.ts) for Cap'n Proto, [`interface.ts`](https://github.com/cloudflare/computer/blob/main/interface.ts) for TypeScript

## Frequently Asked Questions

### What causes "expected runtime X but got Y" errors in Cloudflare Computer?

This error from `assertExecutionRuntime` indicates an execution ID was created in one runtime but accessed from another. The `WorkspaceRuntime` in [`runtime/runtime.ts`](https://github.com/cloudflare/computer/blob/main/runtime/runtime.ts) tracks exec-to-runtime mappings via `#rememberExecutionRuntime`. Fix by ensuring you pass the correct `runtimeId` to `exec`, `pull`, or `push` calls, and don't reuse `Workspace` instances across different runtimes without resetting the internal `#runtime` field.

### Why does my code compile but fail at runtime with type errors?

The `RpcStub` proxy in [`packages/rpc/src/client.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts) performs a nominal cast (`as SyncRPC`) that TypeScript cannot verify across process boundaries. The remote side may implement a different shape while the proxy pretends it's correct. Wrap stubs with `debugRpc()` or use `assertRpcImplements()` to add runtime validation that catches these mismatches immediately.

### How do I debug type issues that only appear in production?

Enable `enableRpcDebug()` to log all RPC traffic as JSON, then inspect the exact payloads. Production environments using `computerd` containers may surface issues masked by worker stubs in development. Run `npm run build` to include native `fuse-native` checks, and reproduce failures in [`packages/computer/tests/runtime.test.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/tests/runtime.test.ts) which exercises real cross-process behavior.

### Where should I add new type assertions for custom RPC methods?

Add runtime guards in [`packages/rpc/src/debug.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/debug.ts) using the existing `assertRpcImplements` pattern. For execution-specific validation, extend [`runtime/types.ts`](https://github.com/cloudflare/computer/blob/main/runtime/types.ts) following the `assertExecutionRuntime` implementation. Always update [`runtime/wire.ts`](https://github.com/cloudflare/computer/blob/main/runtime/wire.ts) and [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts) to keep Cap'n Proto contracts synchronized with TypeScript definitions.