Yeachan-Heo/oh-my-codex · error · Error

pane_session_mismatch

pane_session_mismatch

Error message

pane_session_mismatch:${tmuxPaneId}:${paneSession || "missing"}

What it means

The bridge verifies the bound pane actually belongs to the expected tmux session via display-message #{session_name}. If the pane has moved, died and been recycled, or the id now resolves to another session, this mismatch error is thrown naming the pane and actual session.

Source

Thrown at src/mcp/hermes-bridge.ts:219

  const rawSession = await readSessionState(cwd);
  if (!rawSession || !sessionMatchesRequested(rawSession, sessionId)) return null;
  const tmuxSessionName = optionalString(rawSession.tmux_session_name);
  const tmuxPaneId = optionalString(rawSession.tmux_pane_id);
  if (!tmuxSessionName || !tmuxPaneId) return null;
  if (!isTmuxPaneId(tmuxPaneId)) {
    throw new Error(`unsupported_session_kind:invalid_tmux_pane_binding:${tmuxSessionName}`);
  }

  const execTmux = deps.execTmuxFileSync ?? defaultExecTmuxFileSync;
  try {
    execTmux(["has-session", "-t", tmuxSessionName]);
    const instanceId = String(execTmux(["show-options", "-qv", "-t", tmuxSessionName, OMX_INSTANCE_OPTION]) ?? "").trim();
    if (instanceId !== rawSession.session_id) {
      throw new Error(`tmux_instance_mismatch:${tmuxSessionName}:${instanceId || "missing"}`);
    }
    const paneSession = String(execTmux(["display-message", "-p", "-t", tmuxPaneId, "#{session_name}"]) ?? "").trim();
    if (paneSession !== tmuxSessionName) {
      throw new Error(`pane_session_mismatch:${tmuxPaneId}:${paneSession || "missing"}`);
    }
    for (const argv of buildSendPaneArgvs(tmuxPaneId, prompt, true)) {
      execTmux(argv);
    }
  } catch (error) {
    const message = errorMessage(error);
    const reason = message.startsWith("tmux_instance_mismatch") || message.startsWith("pane_session_mismatch")
      ? message
      : "tmux_send_failed";
    throw new Error(`unsupported_session_kind:tmux_prompt_delivery_failed:${tmuxSessionName}:${tmuxPaneId}:${reason}`);
  }
  return {
    session_id: rawSession.session_id,
    tmux_session_name: tmuxSessionName,
    target: tmuxPaneId,
    transport: "tmux_send_keys",
  };
}

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Restart the session to rebind to a live pane
  2. Prune session state referencing dead panes
  3. Avoid moving panes between sessions while a bridge binding is active
Defensive patterns

Strategy: fallback

Validate before calling

const paneSession = execSync(`tmux display-message -p -t ${paneId} '#{session_name}'`).toString().trim();
if (paneSession !== expectedSession) { /* rebind */ }

Try / catch

catch (e) { if ((e as Error).message.startsWith('pane_session_mismatch')) { /* restart session to obtain fresh pane binding */ } }

Prevention

When it happens

Trigger: execTmux display-message -p -t <paneId> returns a session name different from tmuxSessionName, or empty ('missing') when the pane no longer exists.

Common situations: Pane was killed or swapped in tmux (join-pane/move-pane), session was renamed, or a stale state file points at a recycled pane id.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/80f38c5d6f785aab. Report an issue: GitHub.