Hmbown/CodeWhale · error · anyhow::Error

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

Error message

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

What it means

tmux_session_state classifies `tmux has-session -t <session>` results: success means Present, and stderr matching known patterns ('can't find session:', 'no server running on', or 'error connecting to' plus 'no such file or directory') means Absent. Any other nonzero failure bails with this message, because the session's existence is genuinely unknown and stopping or reconciling on a guess would be unsafe.

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 0c42157ee5)

Solutions

  1. Reproduce manually: tmux -S <socket> has-session -t <session> and read the stderr shown in the message
  2. Fix the reported condition: correct socket dir ownership/permissions, or sanitize the session name (alphanumeric plus dashes)
  3. Move offending user config out of tmux.conf and retry
  4. If the server is wedged, remove the stale socket file so a fresh server can start

Example fix

// before
let session = format!("{}:{}", user_name, raw_title); // ':' breaks targeting

// after
let session: String = raw_title
    .chars()
    .map(|c| if c.is_ascii_alphanumeric() || c == '-' { c } else { '_' })
    .collect();
let session = format!("codewhale-{lane_id}-{session}");
Defensive patterns

Strategy: try-catch

Validate before calling

fn safe_tmux_session_name(name: &str) -> bool {
    !name.is_empty()
        && name
            .chars()
            .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
}

Try / catch

match tmux_session_state(&socket, &session) {
    Ok(state) => { /* Present/Absent handling */ }
    Err(err) if err.to_string().contains("has-session") => {
        // Existence indeterminate: surface to the operator, do not guess.
        log::warn!("tmux state unknown for {session}: {err:#}");
        return Ok(None);
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: has-session exiting nonzero with unrecognized stderr: permission denied on the socket path, a malformed session name (colons/leading dots confuse tmux targeting), tmux server errors, or tmux.conf errors surfacing on first server start.

Common situations: Socket directories under paths with restrictive permissions or different uids (shared machines, sudo drops); session names generated from user input containing tmux target syntax; broken global tmux.conf; leftover sockets from a crashed server with wrong ownership.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/594676a8a309487b. Report an issue: GitHub.