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

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/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/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/packages/computer/src/runtime/types.ts)

Common sources of runtime type failures include:

  • Mismatched RPC contracts — The Cap'n Proto definition in wire.ts diverges from TypeScript declarations
  • Incorrect stub casting — The RpcStub proxy in 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. The test harness spins up a real container (computerd) or worker stub, giving you authentic cross-process behavior.

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

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 against 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, newWebSocketRpcSession performs a double cast:

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

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

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, #rememberExecutionRuntime stores the mapping:

// 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) verify consistency:

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:

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:

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 for Cap'n Proto changes
  2. Adjust 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

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

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

Adding Runtime Shape Assertions

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

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

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 Core WorkspaceRuntime; exec ID tracking, lifecycle management [runtime.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts)
packages/computer/src/runtime/types.ts RPC payload types, exec-runtime assertions, utilities [types.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/types.ts)
packages/computer/src/runtime/wire.ts Cap'n Proto contract between Durable Object and computerd [wire.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/wire.ts)
packages/computer/tests/runtime.test.ts Test suite for runtime layer reproductions [runtime.test.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.test.ts)
packages/rpc/src/client.ts RPC stub creation over WebSocket [client.ts](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/client.ts)
packages/rpc/src/debug.ts enableRpcDebug, debugRpc, assertRpcImplements [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 for Cap'n Proto, 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 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 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 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 using the existing assertRpcImplements pattern. For execution-specific validation, extend runtime/types.ts following the assertExecutionRuntime implementation. Always update runtime/wire.ts and packages/rpc/src/interface.ts to keep Cap'n Proto contracts synchronized with TypeScript definitions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →