affaan-m/ECC · error

tmux session already exists: ${plan.sessionName}

Error message

tmux session already exists: ${plan.sessionName}

What it means

Thrown by executePlan in the tmux worktree orchestrator when plan.replaceExisting is falsy and `tmux has-session -t <sessionName>` exits 0, proving a tmux session with that name is already live. The orchestrator refuses to clobber an existing session so it does not hijack or duplicate panes the user is actively using. Passing replaceExisting (or killing the session first) is the only way past the guard.

Source

Thrown at scripts/lib/tmux-worktree-orchestrator.js:506

  const rollbackCreatedResourcesImpl = runtime.rollbackCreatedResources || rollbackCreatedResources;
  const createdState = {
    workerPlans: [],
    sessionCreated: false,
    removeCoordinationDir: !fs.existsSync(plan.coordinationDir)
  };

  runCommandImpl('git', ['rev-parse', '--is-inside-work-tree'], { cwd: plan.repoRoot });
  runCommandImpl('tmux', ['-V']);

  if (plan.replaceExisting) {
    cleanupExistingImpl(plan);
  } else {
    const hasSession = spawnSyncImpl('tmux', ['has-session', '-t', plan.sessionName], {
      encoding: 'utf8',
      stdio: ['ignore', 'pipe', 'pipe']
    });
    if (hasSession.status === 0) {
      throw new Error(`tmux session already exists: ${plan.sessionName}`);
    }
  }

  try {
    materializePlanImpl(plan);

    for (const workerPlan of plan.workerPlans) {
      runCommandImpl('git', workerPlan.gitArgs, { cwd: plan.repoRoot });
      createdState.workerPlans.push(workerPlan);
      overlaySeedPathsImpl({
        repoRoot: plan.repoRoot,
        seedPaths: workerPlan.seedPaths,
        worktreePath: workerPlan.worktreePath
      });
    }

    runCommandImpl(
      'tmux',

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Kill the existing session: `tmux kill-session -t <plan.sessionName>` then re-run.
  2. Set plan.replaceExisting = true so executePlan runs cleanupExisting (kills session + stale worktrees/branches) before materializing.
  3. Generate a unique sessionName per run (append a short hash or timestamp) if you intentionally want parallel orchestrations.
  4. Run `tmux ls` to confirm which sessions exist and decide whether the old one is safe to destroy.

Example fix

// before
const plan = { sessionName: 'ecc-main', replaceExisting: false, ... };
executePlan(plan);

// after — let the orchestrator reclaim the name
const plan = { sessionName: 'ecc-main', replaceExisting: true, ... };
executePlan(plan);

// or clear it manually first
// shell: tmux kill-session -t ecc-main
Defensive patterns

Strategy: validation

Validate before calling

// Before calling executePlan, check for an existing session and decide.
const { spawnSync } = require('child_process');
function sessionExists(name) {
  const r = spawnSync('tmux', ['has-session', '-t', name], { stdio: 'ignore' });
  return r.status === 0;
}
function safeExecutePlan(plan) {
  if (!plan.replaceExisting && sessionExists(plan.sessionName)) {
    throw new Error(`Refusing to run: tmux session '${plan.sessionName}' exists. Kill it or set replaceExisting=true.`);
  }
  return executePlan(plan);
}

Type guard

// Narrow a plan so executePlan cannot hit the 'already exists' guard unexpectedly.
/** @typedef {{ sessionName: string, replaceExisting?: boolean, repoRoot: string, workerPlans: any[], coordinationDir: string }} Plan */
/**
 * @param {unknown} p
 * @returns {p is Plan & { replaceExisting: true }}
 */
function isReplaceOrFreshPlan(p) {
  return !!p && typeof p === 'object'
    && typeof p.sessionName === 'string'
    && (p.replaceExisting === true || !sessionExistsSafe(p.sessionName));
}

Try / catch

try {
  executePlan(plan);
} catch (err) {
  if (/tmux session already exists/.test(err.message)) {
    if (autoReplace) { executePlan({ ...plan, replaceExisting: true }); }
    else { console.error(err.message); process.exit(2); }
  } else throw err;
}

Prevention

When it happens

Trigger: Calling executePlan(plan) a second time with the same plan.sessionName while the first session is still attached/detached-but-alive. Running two orchestrator plans whose generated session names collide (e.g. same repo slug). A previous run crashed without cleanup, leaving an orphaned tmux session.

Common situations: Re-running `dmux`/orchestration during iterative development after a partial failure. SSH disconnect left a session running. Two terminals pointed at the same worktree plan. CI runner reused a tmux server across jobs without `tmux kill-server`.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/988a709475ab759e. Report an issue: GitHub.