Hmbown/CodeWhale · error

tmux has-session for

Error message

tmux has-session for {session} failed with {}: {}

What it means

`tmux_session_state` runs `tmux has-session -t <session>` against a pinned socket to classify whether a lane's tmux session is Present or Absent. Known-benign stderr patterns ("can't find session:", "no server running on", "error connecting to" + "no such file or directory") are treated as Absent; any other nonzero exit means tmux failed in an unexpected way, so the runtime bails rather than guessing the session state. Callers `stop_tmux_session` and `reconcile` depend on this classification being trustworthy, which is why failure is loud.

Solutions

  1. Run `tmux -S <socket> has-session -t <session>` manually to see the full stderr and diagnose the actual tmux failure.
  2. If the tmux server protocol is stale (version mismatch), kill the old server (`tmux -S <socket> kill-server`) or remove the stale socket file, then retry the stop/reconcile.
  3. Check socket permissions/ownership — ensure the runtime process runs as the same user that created the session.
  4. If the session is genuinely gone but tmux errors oddly, remove the lane record or clear its pinned tmux_socket/tmux_session metadata so reconcile treats it as absent.

Example fix

// diagnose
$ tmux -S /run/codewhale/lane.sock has-session -t lane-42
# error connecting to /run/codewhale/lane.sock: Permission denied
$ sudo rm /run/codewhale/lane.sock   # stale socket owned by another user
Defensive patterns

Strategy: try-catch

Validate before calling

let out = std::process::Command::new("tmux")
    .args(["-S", socket, "has-session", "-t", session])
    .output()
    .map_err(|e| e.to_string())?;
if !out.status.success() && !is_benign_absence(&String::from_utf8_lossy(&out.stderr)) {
    return Err(String::from_utf8_lossy(&out.stderr).into());
}

Prevention

When it happens

Trigger: Calling `stop` or `reconcile` on a tmux lane when `tmux has-session` exits nonzero with stderr that matches none of the benign absence patterns — e.g. the socket path is stale/unreachable, tmux reports a protocol-version mismatch with an existing server, the socket file has wrong permissions, or tmux is corrupted.

Common situations: A tmux server was upgraded between runs so the on-disk socket speaks an incompatible protocol; the pinned socket path was deleted or recreated with different permissions; a stale socket directory with a dead server that tmux cannot clean up; running the runtime as a different user who cannot access the socket.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/594676a8a309487b. Report an issue: GitHub.

Appendix: source

Thrown at crates/lane/src/runtime.rs:588

fn tmux_session_state(socket: &Path, session: &str) -> Result<TmuxSessionState> {
    let output = tmux_command(socket)
        .args(["has-session", "-t", session])
        .stdout(Stdio::null())
        .stderr(Stdio::piped())
        .output()
        .with_context(|| format!("query tmux session {session}"))?;
    if output.status.success() {
        return Ok(TmuxSessionState::Present);
    }
    let stderr = String::from_utf8_lossy(&output.stderr).to_ascii_lowercase();
    if stderr.contains("can't find session:")
        || stderr.contains("no server running on")
        || (stderr.contains("error connecting to") && stderr.contains("no such file or directory"))
    {
        return Ok(TmuxSessionState::Absent);
    }
    bail!(
        "tmux has-session for {session} failed with {}: {}",
        output.status,
        stderr.trim()
    )
}

fn stop_tmux_session(socket: &Path, session: &str) -> Result<()> {
    let status = tmux_command(socket)
        .args(["kill-session", "-t", session])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .with_context(|| format!("kill tmux session {session}"))?;
    match tmux_session_state(socket, session).with_context(|| {
        format!(
            "confirm tmux session {session} on {} stopped after kill-session ({status})",
            socket.display()
        )

View on GitHub (pinned to 73e0f67d83)