{"record":{"id":"6abcd2c70ba1d759","repo":"Hmbown/CodeWhale","slug":"duration-must-be-0-05-30-seconds","errorCode":null,"errorMessage":"duration must be 0.05..30 seconds","messagePattern":"duration must be 0\\.05\\.\\.30 seconds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/tools.mjs","lineNumber":826,"sourceCode":"      return { name: wire, args: { ...rest, computer: id } };\n    }\n    case \"consent\": {\n      const rest = { ...args };\n      delete rest.action;\n      const wire = { status: \"consent_status\", allow: \"consent_allow\", deny: \"consent_deny\", revoke: \"consent_revoke\" }[args.action];\n      if (!wire) throw bad(`consent action must be status, allow, deny or revoke (got ${JSON.stringify(args.action)})`);\n      if (args.action === \"status\") return { name: wire, args: { computer: rest.computer } };\n      const foreground = rest.scope === \"foreground\";\n      if (!foreground && rest.app == null && rest.name == null && rest.bundle_id == null && rest.pid == null) {\n        throw bad(`consent action \"${args.action}\" needs an app (name, bundle_id, pid or app string) — or scope:\"foreground\" for the shared-pointer decision`);\n      }\n      return { name: wire, args: rest };\n    }\n    case \"key\": {\n      if (args.duration == null) return { name, args };\n      const { duration, repeat, target, ...rest } = args;\n      if (repeat != null || target != null) throw bad(\"key with duration holds the key — repeat and target cannot be combined with it\");\n      if (!Number.isFinite(duration) || duration < 0.05 || duration > 30) throw bad(\"duration must be 0.05..30 seconds\");\n      return { name: \"hold_key\", args: { ...rest, duration } };\n    }\n    case \"browser\": {\n      const rest = { ...args };\n      delete rest.action;\n      switch (args.action) {\n        case \"start\":\n          if (rest.url != null && typeof rest.url !== \"string\") throw bad(\"browser start url must be a string\");\n          return { name: \"browser_start\", args: rest };\n        case \"status\": return { name: \"browser_status\", args: rest };\n        case \"navigate\":\n          if (typeof rest.url !== \"string\" || !rest.url.trim()) throw bad('browser action \"navigate\" requires url');\n          return { name: \"browser_navigate\", args: rest };\n        case \"click\": {\n          const hasSelector = typeof rest.selector === \"string\" && rest.selector.trim();\n          const hasPoint = rest.point != null && Number.isFinite(rest.point?.x) && Number.isFinite(rest.point?.y);\n          if (hasSelector && hasPoint) throw bad('browser action \"click\" takes selector or point, not both — pick one target');\n          if (!hasSelector && !hasPoint) throw bad('browser action \"click\" needs selector (CSS) or point {x,y}');","sourceCodeStart":808,"sourceCodeEnd":844,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/tools.mjs#L808-L844","documentation":"Argument-validation guard in resolveTool's 'key' case: when duration is supplied, it is checked with Number.isFinite against the 0.05–30 second domain. NaN, Infinity, or out-of-range durations are rejected before the hold_key command is built.","triggerScenarios":"Thrown at crates/tui/plugins/computer-use/src/tools.mjs:826 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Clamp duration to between 0.05 and 30 seconds.","Use a plain key press (no duration) for instantaneous taps."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}