Hmbown/CodeWhale · error

key with duration holds the key — repeat and target cannot…

Error message

key with duration holds the key — repeat and target cannot be combined with it

What it means

resolveTool's key case: a duration makes the key a held key (hold_key), which is incompatible with repeat and target semantics; combining them is rejected rather than silently dropping one of the arguments.

Solutions

  1. Drop either duration or the repeat/target pair — a held key cannot also repeat or retarget.
  2. Issue separate key presses for repeats instead of combining with a hold duration.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/tui/plugins/computer-use/src/tools.mjs:825 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

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

      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) {
        case "start":
          if (rest.url != null && typeof rest.url !== "string") throw bad("browser start url must be a string");
          return { name: "browser_start", args: rest };
        case "status": return { name: "browser_status", args: rest };
        case "navigate":
          if (typeof rest.url !== "string" || !rest.url.trim()) throw bad('browser action "navigate" requires url');
          return { name: "browser_navigate", args: rest };
        case "click": {
          const hasSelector = typeof rest.selector === "string" && rest.selector.trim();
          const hasPoint = rest.point != null && Number.isFinite(rest.point?.x) && Number.isFinite(rest.point?.y);
          if (hasSelector && hasPoint) throw bad('browser action "click" takes selector or point, not both — pick one target');

View on GitHub (pinned to 73e0f67d83)