{"record":{"id":"89610567dabe8390","repo":"Hmbown/CodeWhale","slug":"ssh-userhost-exited-r-code-r-stderr-trim-slice-0-400","errorCode":null,"errorMessage":"ssh ${userHost} exited ${r.code}: ${r.stderr.trim().slice(0, 400)}","messagePattern":"ssh (.+?) exited (.+?): (.+?)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/transport.mjs","lineNumber":223,"sourceCode":"  const portArgs = computer.port ? [\"-p\", String(computer.port)] : [];\n  const remoteAgent = safeRemotePath(computer.agentPath ?? \".codewhale-cu/agent/agent.mjs\");\n  const base = [\"-o\", \"BatchMode=yes\", \"-o\", \"ConnectTimeout=8\", \"-o\", \"StrictHostKeyChecking=accept-new\", ...portArgs, userHost];\n  const ex = {\n    kind: \"ssh\",\n    base,\n    userHost,\n    remoteAgent,\n    run(cmd, args = [], opts = {}) {\n      // Local side commands (e.g. ssh itself) run directly.\n      return run(cmd, args, opts);\n    },\n    async remote(request, opts = {}) {\n      const r = await run(\"ssh\", [...base, \"node\", remoteAgent, b64({ args: request.args ?? {}, tool: request.tool, nonce: crypto.randomBytes(6).toString(\"hex\") })], {\n        timeoutMs: opts.timeoutMs ?? 25_000,\n      });\n      if (r.aborted) throw Object.assign(new ExecError(\"computer request cancelled\", r), { code: \"cancelled\" });\n      if (r.timedOut) throw new ExecError(`ssh ${userHost}: timed out`, r);\n      if (r.code !== 0) throw new ExecError(`ssh ${userHost} exited ${r.code}: ${r.stderr.trim().slice(0, 400)}`, r);\n      // The agent prints exactly one JSON line; anything before it is MOTD noise.\n      const line = r.stdout.trim().split(\"\\n\").filter((l) => l.startsWith(\"{\")).pop();\n      const reply = line ? JSON.parse(line) : null;\n      if (!reply) throw new ExecError(`ssh ${userHost}: agent returned no JSON receipt`, r);\n      return reply;\n    },\n  };\n  attachPersistentChannel(ex, binding, [\"ssh\", ...base, \"node\", remoteAgent, \"--serve\"]);\n  return ex;\n}\n\n/**\n * docker executor: a spawned task-owned desktop container. Same agent contract\n * as ssh, but the channel is `docker exec` — no sshd, no keys, the container\n * boundary itself is the isolation. Every call goes through\n * docker/agent-exec.sh, which joins the desktop session env (display + bus)\n * the container entrypoint recorded before serving requests.\n */","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/transport.mjs#L205-L241","documentation":"The computer-use plugin's SSH transport runs the remote agent over `ssh node <agent> <base64 args>` and throws this ExecError when the ssh process exits with a nonzero status. The stderr (trimmed, max 400 chars) is embedded so the developer sees the remote ssh failure cause. It means the remote command never produced a usable run — ssh connectivity, auth, or the remote node invocation failed before a receipt could be parsed.","triggerScenarios":"Calling executor.remote() when ssh exits nonzero: host unreachable, auth rejected, remote node binary missing, remoteAgent script path missing, or remote node crashes with nonzero exit.","commonSituations":"SSH key not deployed to the remote machine; wrong userHost/port in the computer binding; remote agent not yet installed at .codewhale-cu/agent; node not on PATH on the remote host; MOTD/shell rc printing to stderr causing ssh -t failures; network/firewall drops mid-run.","solutions":["Test connectivity manually: ssh <userHost> 'node --version' to reproduce and see full stderr","Verify the agent is installed by running installRemoteAgent() first (creates .codewhale-cu/agent)","Confirm node is installed and on PATH on the remote host","Check userHost, port, and key config in the computer binding","Inspect the stderr embedded in the error message for the root cause (auth denied, no route, etc.)"],"exampleFix":"// before\nconst ex = sshExec({ transport: \"ssh\" }); // host wrong / agent missing\nawait ex.remote({ tool: \"platform\" });\n// after\nconst ex = sshExec({ transport: \"ssh\", userHost: \"deploy@10.0.0.5\", port: 22 });\nawait installRemoteAgent(ex, computer); // ensure agent exists before remote calls\nawait ex.remote({ tool: \"platform\" });","handlingStrategy":"try-catch","validationCode":"// pre-check ssh reachability and agent presence\nconst probe = await run(\"ssh\", [...base, \"test\", \"-x\", \".codewhale-cu/agent/agent.mjs\"]);\nif (probe.code !== 0) await installRemoteAgent(ex, computer);","typeGuard":"function isSshExecError(e) { return e instanceof ExecError && /^ssh .* exited \\d+:/i.test(e.message); }","tryCatchPattern":"try {\n  const reply = await ex.remote(request, opts);\n} catch (e) {\n  if (e.code === \"cancelled\") return;\n  if (/exited \\d+:/.test(e.message)) console.error(\"ssh failed:\", e.message); // stderr embedded\n}","preventionTips":["Install the remote agent before issuing remote calls","Verify ssh key auth works to userHost before registering the computer","Confirm node is on PATH on the remote host","Keep timeouts generous enough for slow links"],"tags":["ssh","network","remote-execution"],"backgroundTag":"ssh-command-failed","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"}