Hmbown/CodeWhale · error
computer action must be list, switch, register, spawn or…
Error message
computer action must be list, switch, register, spawn or remove (got ${JSON.stringify(args.action)}) What it means
Argument-validation guard in resolveTool for the 'computer' action: args.action matched none of list/switch/register/spawn/remove in the action-to-wire-name map, so no backend command name could be resolved. The received value is shown in the message.
Solutions
- Use one of: list, switch, register, spawn, remove
- Lowercase the action value
- Map "connect" to "switch" and "delete" to "remove" in the caller
Example fix
// before
resolveTool("computer", { action: "connect", id: "vm1" })
// after
resolveTool("computer", { action: "switch", id: "vm1" }) Defensive patterns
Strategy: validation
Validate before calling
if (!['list','switch','register','spawn','remove'].includes(args.action)) throw new Error('computer action must be list/switch/register/spawn/remove'); Type guard
function isComputerAction(a) { return ['list','switch','register','spawn','remove'].includes(a); } Try / catch
try { resolveTool('computer', args); } catch (e) { if (String(e.message).includes('computer action')) { /* map synonyms connect->switch, delete->remove and retry */ } else throw e; } Prevention
- Map common synonyms (connect/delete) to supported actions before calling
- Normalize action casing
- Consult the tool schema's action enum
When it happens
Trigger: resolveTool("computer", { action: "delete" }) or { action: "Connect" } or {} with no action.
Common situations: A model invents "connect" or "delete" (should be switch/remove); missing action field; wrong casing.
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
- agent profile reasoning_effort must be one of: inherit…
- Choose approve or decline
- clipboard action must be "read" or "write
- computer action " " requires id
- consent action must be status, allow, deny or revoke
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8dc096207774da3a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/tools.mjs:803
if (args.action === "write") {
if (typeof rest.text !== "string") throw bad("clipboard action \"write\" requires text");
return { name: "write_clipboard", args: { text: rest.text, computer: rest.computer } };
}
throw bad(`clipboard action must be "read" or "write" (got ${JSON.stringify(args.action)})`);
}
case "recording": {
const rest = { ...args };
delete rest.action;
const wire = { start: "recording_start", stop: "recording_stop", status: "recording_status", list: "recording_list" }[args.action];
if (!wire) throw bad(`recording action must be start, stop, status or list (got ${JSON.stringify(args.action)})`);
if ((args.action === "stop" || args.action === "status") && rest.id == null) throw bad(`recording action "${args.action}" requires id`);
return { name: wire, args: rest };
}
case "computer": {
const rest = { ...args };
delete rest.action;
const wire = { list: "computer_list", switch: "computer_switch", register: "computer_register", spawn: "computer_spawn", remove: "computer_remove" }[args.action];
if (!wire) throw bad(`computer action must be list, switch, register, spawn or remove (got ${JSON.stringify(args.action)})`);
if (args.action === "list") return { name: wire, args: {} };
if (rest.id == null) throw bad(`computer action "${args.action}" requires id`);
const id = rest.id;
delete rest.id;
return { name: wire, args: { ...rest, computer: id } };
}
case "consent": {
const rest = { ...args };
delete rest.action;
const wire = { status: "consent_status", allow: "consent_allow", deny: "consent_deny", revoke: "consent_revoke" }[args.action];
if (!wire) throw bad(`consent action must be status, allow, deny or revoke (got ${JSON.stringify(args.action)})`);
if (args.action === "status") return { name: wire, args: { computer: rest.computer } };
const foreground = rest.scope === "foreground";
if (!foreground && rest.app == null && rest.name == null && rest.bundle_id == null && rest.pid == null) {
throw bad(`consent action "${args.action}" needs an app (name, bundle_id, pid or app string) — or scope:"foreground" for the shared-pointer decision`);
}
return { name: wire, args: rest };
}View on GitHub (pinned to 73e0f67d83)