{"record":{"id":"2310e36f0f7b1239","repo":"Hmbown/CodeWhale","slug":"name-must-be-a-finite-number","errorCode":null,"errorMessage":"${name} must be a finite number","messagePattern":"(.+?) must be a finite number","errorType":"validation","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":206,"sourceCode":"    const args = mods.flatMap((mod) => [\"-M\", mod]);\n    for (let i = 0; i < repeat; i++) {\n      args.push(onlyModifier ? \"-M\" : \"-P\", onlyModifier ? modKey : key);\n      if (holdMs) args.push(\"-s\", String(holdMs));\n      args.push(onlyModifier ? \"-m\" : \"-p\", onlyModifier ? modKey : key);\n    }\n    args.push(...mods.reverse().flatMap((mod) => [\"-m\", mod]));\n    // wtype owns a temporary Wayland keyboard; the compositor releases its\n    // keys on process exit, including cancellation. Keep the complete gesture\n    // in one process (https://github.com/atx/wtype#usage).\n    throwIfAborted();\n    const result = await run(\"wtype\", args, { timeoutMs: Math.max(10_000, holdMs + 8_000) });\n    throwIfAborted();\n    if (result.code !== 0) throw new ExecError(`wtype exited ${result.code}: ${result.stderr.trim().slice(0, 200)}`, result);\n  }\n\n  function assertNum(v, name) {\n    const n = Number(v);\n    if (!Number.isFinite(n)) throw new ExecError(`${name} must be a finite number`);\n    return n;\n  }\n\n  // ---------- AT-SPI tree ----------\n  const PYATSPI_APP = `def resolve_app(desktop, app_name):\n    apps = [desktop.getChildAtIndex(i) for i in range(desktop.childCount)]\n    if app_name:\n        matches = [app for app in apps if app and app_name.casefold() == (app.name or \"\").casefold()]\n        return matches[0] if len(matches) == 1 else None\n    return next((app for app in apps if app and app.childCount), None)\n`;\n  const PYATSPI_WALK = `import json, sys, pyatspi\n${PYATSPI_APP}\napp_name = sys.argv[1] if len(sys.argv) > 1 else None\ndepth_max = int(sys.argv[2]) if len(sys.argv) > 2 else 8\nmax_el = int(sys.argv[3]) if len(sys.argv) > 3 else 400\ndesktop = pyatspi.Registry.getDesktop(0)\nroot = resolve_app(desktop, app_name)","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L188-L224","documentation":"A synchronous argument validation helper: assertNum(v, name) coerces via Number() and throws when the result is not finite (NaN, Infinity, or non-numeric strings). It exists so numeric parameters like coordinates, sizes, and repeat counts fail fast with a message naming the offending parameter.","triggerScenarios":"Passing a non-numeric or non-finite value where a number is required: undefined coordinates from a malformed action payload, strings like '100px', null, NaN from upstream math, or a region element that is not a number.","commonSituations":"Tool-call JSON with coordinates as strings ('x': '120'); deriving a coordinate from a division that produced Infinity; forgetting a required field so undefined reaches Number(undefined) === NaN; region arrays built from regex matches that were not parseFloat'ed.","solutions":["Coerce and validate inputs before calling the action: Number(value) and Number.isFinite checks at your boundary.","Ensure required numeric fields are present in the action payload (missing fields become NaN here).","Strip units from strings ('100px' -> 100) before passing.","For computed values, guard divisions by zero that yield Infinity.","Read the message's parameter name — it tells you exactly which argument was bad."],"exampleFix":"// before\nawait move({ x: req.x, y: req.y }); // x may be '120px'\n// after\nconst x = Number.parseFloat(req.x), y = Number(req.y);\nif (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error('bad coords');\nawait move({ x, y });","handlingStrategy":"validation","validationCode":"const assertFinite = (v, name) => {\n  const n = Number(v);\n  if (!Number.isFinite(n)) throw new TypeError(`${name} must be a finite number, got ${String(v)}`);\n  return n;\n};\n// call before invoking any action: assertFinite(payload.x, 'x'), etc.","typeGuard":"const isFiniteNumber = (v) => typeof v === 'number' && Number.isFinite(v);\n// or for numeric strings: const isCoercible = (v) => Number.isFinite(Number(v));","tryCatchPattern":"try {\n  await clickAt({ x: payload.x, y: payload.y });\n} catch (e) {\n  if (/must be a finite number/.test(e.message)) {\n    const name = e.message.split(' ')[0];\n    console.error(`Fix payload field '${name}': must be numeric, got`, payload[name.trim()]);\n  } else throw e;\n}","preventionTips":["Validate action payloads with a schema (zod/ajv) that enforces finite numbers.","Parse numeric strings and strip units at your boundary.","Guard computed coordinates against division by zero / NaN propagation.","Treat missing required fields as errors before dispatch (undefined -> NaN here).","Log the raw payload when this fires — it usually indicates upstream serialization drift."],"tags":["validation","arguments","numeric"],"backgroundTag":"invalid-argument-value","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"}