{"record":{"id":"cf184800e706c5da","repo":"Hmbown/CodeWhale","slug":"durationsec-must-be-positive","errorCode":null,"errorMessage":"durationSec must be positive","messagePattern":"durationSec must be positive","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":637,"sourceCode":"    // start through the same window_info the AX path uses, so a background\n    // window records behind the user's work. The rect is fixed at start —\n    // it does not track later moves or resizes.\n    let window = null;\n    if (app_ref !== undefined || window_id != null) {\n      window = await native(\"window_info\", { app_ref: app_ref === undefined ? state.inputApp ?? undefined : app_ref, window_id });\n      if (!window?.points || !(window.points.w > 0) || !(window.points.h > 0)) throw new ExecError(\"the selected application has no capturable window — call list_windows\");\n      if (region) throw new ExecError(\"choose app_ref or region, not both\");\n      region = [window.points.x, window.points.y, window.points.w, window.points.h];\n    }\n    let disp = display ?? state.activeDisplay;\n    if (window && display == null) {\n      const cx = region[0] + region[2] / 2, cy = region[1] + region[3] / 2;\n      const host = displays.find(d => d.points && cx >= d.points.x && cx < d.points.x + d.points.w && cy >= d.points.y && cy < d.points.y + d.points.h);\n      if (host) disp = host.index;\n    }\n    const selected = displays.find(d => d.index === disp);\n    if (!selected) throw new ExecError(\"choose one available display for recording\");\n    if (durationSec != null && (!Number.isFinite(durationSec) || durationSec <= 0)) throw new ExecError(\"durationSec must be positive\");\n    const capabilities = await native(\"input_capabilities\");\n    if (capabilities?.record_owner_pipe !== 1) throw new ExecError(\"native screen recorder cannot own its client lifetime; update Computer Use before recording\");\n    const helper = await nativeHelper();\n    throwIfAborted();\n    const child = spawn(helper, [JSON.stringify({ tool: \"record\", args: { file, displayID: selected.id, region, durationSec, owner_pipe: true } })], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n    child.stdin.on(\"error\", () => {});\n    const startedAt = new Date().toISOString();\n    let stderr = \"\", output = \"\", ready = false;\n    const completion = new Promise(resolve => {\n      child.once(\"error\", error => resolve({ code: -1, error: error.message }));\n      child.once(\"close\", code => resolve({ code, error: stderr.trim() }));\n    });\n    child.stderr.on(\"data\", chunk => { stderr = (stderr + chunk).slice(-4000); });\n    const recording = { child, completion, pid: child.pid, file, startedAt, mode: \"ScreenCaptureKit\", display: disp };\n    rec.set(id, recording);\n    const signal = currentSignal();\n    let timer, abort;\n    try {","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L619-L655","documentation":"The record action validates the optional durationSec: when provided, it must be a finite number greater than 0. Null/undefined (unbounded recording) is allowed; NaN, Infinity, zero, negatives, or non-numbers throw before the native recorder is spawned.","triggerScenarios":"Calling record({ durationSec: 0 }) or a negative value; durationSec arriving as a string ('30') or as null-coerced value from config; passing Infinity from a computed timeout.","commonSituations":"Config files parsed as strings; an agent computing duration via division producing NaN; UI input allowing 0 meaning 'default' while the API expects null for unbounded.","solutions":["Pass a positive finite number, e.g. record({ durationSec: 30 }).","Use null/undefined for unbounded recording instead of 0.","Coerce and validate: Number(durationSec) then check Number.isFinite and > 0 before calling."],"exampleFix":"// before\nawait record({ durationSec: \"30\" });\n// after\nawait record({ durationSec: 30 });","handlingStrategy":"validation","validationCode":"if (durationSec != null && !(Number.isFinite(durationSec) && durationSec > 0))\n  throw new Error(\"durationSec must be a positive finite number or null\");","typeGuard":"const isValidDuration = (d) => d == null || (typeof d === \"number\" && Number.isFinite(d) && d > 0);","tryCatchPattern":"try { await record({ durationSec }) } catch (e) {\n  if (String(e.message).includes(\"durationSec must be\")) {\n    await record({ durationSec: Number(durationSec) > 0 ? Number(durationSec) : null });\n  } else throw e;\n}","preventionTips":["Coerce config-driven durations with Number() and validate finiteness before calling.","Use null for unbounded recording instead of 0.","Guard computed durations against NaN/Infinity from division."],"tags":["macos","recording","validation"],"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-22T11:17:16.035Z"}