Hmbown/CodeWhale · error · ExecError
unknown transport
Error message
unknown transport ${computer.transport} What it means
executorFor() dispatches a computer binding to its transport implementation (local, ssh, docker, hdc) and throws this ExecError for any transport value outside the supported set. It is a fail-loud guard against misconfigured computer registrations, per the misconfiguration-fails-loud convention.
Solutions
- Fix computer.transport in the config to one of: ssh, docker, hdc (or a valid local setup)
- Check for case/typo mistakes — the value is matched exactly
- If migrating from an older config, update the transport name to the current set
- Add validation at config-load time to reject unknown transports early
- Log/inspect the actual computer object to see the offending value
Example fix
// before
{ "computer": { "transport": "ssh " } } // trailing space -> unknown transport
// after
{ "computer": { "transport": "ssh" } } Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED = new Set(["ssh", "docker", "hdc"]);
if (!SUPPORTED.has(computer?.transport)) throw new Error(`transport must be one of ssh|docker|hdc, got ${computer?.transport}`);
const ex = executorFor(computer); Type guard
function isKnownTransport(c) { return c && ["ssh", "docker", "hdc"].includes(c.transport); } Try / catch
try {
const ex = executorFor(computer);
} catch (e) {
if (e.message.startsWith("unknown transport")) {
console.error(`bad transport in binding: ${JSON.stringify(computer)}`); // fail loud, show config
throw e;
}
throw e;
} Prevention
- Validate computer bindings at config load, before dispatch
- Use only exact lowercase transport names: ssh, docker, hdc
- Trim user-supplied config strings to avoid trailing-space mismatches
- Migrate configs from removed transports to the current set
When it happens
Trigger: Calling executorFor(computer) where computer.transport is anything other than "ssh"|"docker"|"hdc" (and not handled by the local path above): typos like "local", "docker2", null/undefined transport, or stale saved config from an older schema.
Common situations: Typo in a config file's transport field; config written by hand or by an older plugin version using a removed transport name; programmatically constructed binding with a wrong string; JSON config with casing differences ("SSH" vs "ssh").
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- 127
- A pinned task provider requires an explicit model
- agent profile reasoning_effort must be one of: inherit…
- approval log has no parent
- --bridge is required in --non-interactive mode.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8bc61392f5ced3b6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/transport.mjs:349
if (computer.transport === "local") {
// Test hook: exercise the out-of-process wire path (desktop app / ssh
// agent) in-process, so wire argument preparation is covered by tests.
if (process.env.CODEWHALE_CU_TEST_REMOTE === "1") {
const { handle } = await import("./app-handler.mjs");
return { ...appExec({ id: "test", name: "test app" }), remote: (request) => handle(request, { sessionId: SESSION_ID, signal: currentSignal() }) };
}
const status = await ensureApp();
if (status.via === "app") {
if (status.app.sessionProtocol !== 2) throw Object.assign(new ExecError("The installed Computer Use helper needs an update for isolated sessions and disconnect cleanup. Rebuild/reinstall it, then retry."), { code: "app_upgrade_required" });
if (process.platform === "darwin" && status.app.backgroundProtocol !== 1) throw Object.assign(new ExecError("The installed Computer Use app predates background scrolling, scoped observations and foreground preemption. Update and restart the helper before using it."), { code: "app_upgrade_required" });
return appExec(status.app);
}
return { ...localExec(), appReason: status.reason };
}
if (computer.transport === "ssh") return sshExec(computer, binding);
if (computer.transport === "docker") return dockerExec(computer, binding);
if (computer.transport === "hdc") return hdcExec(computer);
throw new ExecError(`unknown transport ${computer.transport}`);
}
/**
* Map a computer to its backend module. Local platform is fixed; ssh
* computers may carry platformHint (probed at registration).
*/
function effectivePlatform(computer) {
let platform = computer.platform ?? computer.platformHint;
if (!platform) {
if (computer.transport === "local") platform = process.platform;
else if (computer.transport === "hdc") platform = "harmonyos";
else if (computer.transport === "docker") platform = "linux"; // spawned containers are always the Linux desktop image
else platform = "linux"; // conservative default for ssh; registration probes it
}
return platform;
}
/** Identity of the effective route, excluding catalog presentation metadata. */View on GitHub (pinned to 73e0f67d83)