Hmbown/CodeWhale · error

consent action must be status, allow, deny or revoke

Error message

consent action must be status, allow, deny or revoke (got ${JSON.stringify(args.action)})

What it means

Argument-validation guard in resolveTool for the 'consent' action: args.action matched none of status/allow/deny/revoke in the wire-name map, so the consent request cannot be mapped to a backend command. The bad value is echoed.

Solutions

  1. Use one of the four actions: status, allow, deny, revoke
  2. Lowercase the action value
  3. Map "grant" to "allow" and "block" to "deny" in caller code

Example fix

// before
resolveTool("consent", { action: "grant", app: "Notes" })
// after
resolveTool("consent", { action: "allow", app: "Notes" })
Defensive patterns

Strategy: validation

Validate before calling

if (!['status','allow','deny','revoke'].includes(args.action)) throw new Error('consent action must be status/allow/deny/revoke');

Type guard

function isConsentAction(a) { return ['status','allow','deny','revoke'].includes(a); }

Try / catch

try { resolveTool('consent', args); } catch (e) { if (String(e.message).includes('consent action')) { /* map grant->allow, block->deny and retry */ } else throw e; }

Prevention

When it happens

Trigger: resolveTool("consent", { action: "grant" }) or { action: "Allow" } or {} with no action.

Common situations: A model uses "grant"/"block" instead of allow/deny; missing action; case mismatch.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/a65fdfa05fedb66e. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/tools.mjs:814

      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 };
    }
    case "key": {
      if (args.duration == null) return { name, args };
      const { duration, repeat, target, ...rest } = args;
      if (repeat != null || target != null) throw bad("key with duration holds the key — repeat and target cannot be combined with it");
      if (!Number.isFinite(duration) || duration < 0.05 || duration > 30) throw bad("duration must be 0.05..30 seconds");
      return { name: "hold_key", args: { ...rest, duration } };
    }
    case "browser": {
      const rest = { ...args };
      delete rest.action;
      switch (args.action) {

View on GitHub (pinned to 73e0f67d83)