{"record":{"id":"3f97c932538a39e7","repo":"Hmbown/CodeWhale","slug":"native-input-owner-did-not-confirm-a-live-input-lease","errorCode":null,"errorMessage":"Native input owner did not confirm a live input lease","messagePattern":"Native input owner did not confirm a live input lease","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/exec.mjs","lineNumber":128,"sourceCode":"  const release = async (message = {}) => {\n    if (!stopped) {\n      stopped = true;\n      if (!closed) {\n        child?.stdin.end(JSON.stringify({ ...message, release: true }) + \"\\n\");\n        killTimer = setTimeout(() => {\n          child?.kill(\"SIGTERM\");\n          killTimer = setTimeout(() => child?.kill(\"SIGKILL\"), 750);\n        }, 750);\n      }\n    }\n    const result = await completion;\n    clearTimeout(killTimer);\n    if (result.code !== 0) throw Object.assign(new ExecError(result.stderr || \"Native input cleanup failed\", result), { code: result.aborted ? \"cancelled\" : \"input_release_failed\" });\n  };\n  let timer;\n  try {\n    const receipt = await Promise.race([ready, new Promise((_, reject) => { timer = setTimeout(() => reject(new ExecError(\"Native input owner did not acknowledge input\")), opts.timeoutMs ?? 20_000); })]);\n    if (receipt?.action_sent !== true || receipt?.input_lease !== true) throw new ExecError(\"Native input owner did not confirm a live input lease\");\n    return { receipt, release, async send(message) {\n      const signal = currentSignal();\n      let commandTimer, abort;\n      try {\n        throwIfAborted(signal);\n        if (closed || stopped || child?.exitCode !== null || child?.signalCode) throw Object.assign(new ExecError(\"Native input owner is closed\"), { code: \"input_owner_closed\" });\n        const next = reply();\n        const cancelled = new Promise((_, reject) => {\n          abort = () => reject(Object.assign(new ExecError(\"computer request cancelled\"), { code: \"cancelled\" }));\n          signal?.addEventListener(\"abort\", abort, { once: true });\n        });\n        child.stdin.write(JSON.stringify(message) + \"\\n\");\n        return await Promise.race([next, cancelled, new Promise((_, reject) => { commandTimer = setTimeout(() => reject(new ExecError(\"Native input owner did not acknowledge pointer motion\")), opts.timeoutMs ?? 20_000); })]);\n      }\n      catch (error) { await release().catch(() => {}); throw error; }\n      finally { clearTimeout(commandTimer); signal?.removeEventListener(\"abort\", abort); }\n    } };\n  } catch (error) {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/exec.mjs#L110-L146","documentation":"runInputLease spawns a native input-owner process and waits for a handshake receipt. The lease is only accepted if the receipt carries action_sent===true AND input_lease===true; anything else (missing fields, wrong values, an unexpected first message) throws this error. It means the native owner started but never confirmed it holds a live, exclusive input lease, so the backend refuses to route input through it.","triggerScenarios":"The native input owner binary starts but its first JSON message lacks input_lease:true or action_sent:true; the owner sends a non-receipt message (log line, warning) that gets parsed as the receipt; a version mismatch between the JS driver and the native owner's handshake protocol; the owner crashes after printing a partial message.","commonSituations":"Out-of-date or mismatched native helper binary after an upgrade; environment (sandbox, missing display permissions) preventing the owner from acquiring input; the owner emitting startup warnings to stdout before its JSON receipt, corrupting the handshake parse.","solutions":["Check the native input owner's version matches the driver — upgrade or reinstall the helper binary so the handshake protocol agrees.","Inspect the owner's stdout/stderr for startup errors (display/permission failures) and fix the environment it runs in.","Ensure nothing else holds exclusive input (another automation session, remote-control tool) that blocks the lease.","Retry; if the failure persists, capture the owner's first output line to confirm whether the handshake shape changed."],"exampleFix":"// caller-side guard\nconst lease = await runInputLease(cmd, args, { timeoutMs: 20000 }).catch((e) => {\n  if (String(e.message).includes('did not confirm a live input lease')) {\n    return runInputLease(rebuiltCmd, [], { timeoutMs: 30000 }); // re-spawn after fixing owner\n  }\n  throw e;\n});","handlingStrategy":"retry","validationCode":null,"typeGuard":"const isLeaseHandshakeError = (e) =>\n  e instanceof Error && /did not confirm a live input lease|did not acknowledge input/.test(e.message);","tryCatchPattern":"try {\n  lease = await runInputLease(cmd, args, { timeoutMs: 20000 });\n} catch (e) {\n  if (isLeaseHandshakeError(e)) {\n    await killStaleOwner();\n    lease = await runInputLease(cmd, args, { timeoutMs: 20000 }); // one retry\n  } else throw e;\n}","preventionTips":["Keep the native input-owner binary version-locked with the JS driver.","Capture and log the owner's stdout to detect protocol or startup changes early.","Ensure no other tool holds exclusive input on the session.","Verify display/input permissions in sandboxed or service contexts before spawning the owner."],"tags":["ipc","handshake","input","process"],"backgroundTag":"unexpected-response-shape","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"}