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
- Kill the existing session: `tmux kill-session -t <plan.sessionName>` then re-run.
- Set plan.replaceExisting = true so executePlan runs cleanupExisting (kills session + stale worktrees/branches) before materializing.
- Generate a unique sessionName per run (append a short hash or timestamp) if you intentionally want parallel orchestrations.
- 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
- Generate a unique sessionName per orchestration run (append a short hash).
- Default plan.replaceExisting to true in CI where stale sessions are expected.
- Run `tmux kill-session -t <name>` or `tmux kill-server` in teardown hooks.
- Before invoking executePlan, log `tmux ls` so collisions are visible in logs.
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
- tmux split-window did not return a pane id for ${workerPlan.
- launcherCommand must be a non-empty string
- Unknown template variable: ${key}
- buildOrchestrationPlan requires at least one worker
- ${program} ${args.join(' ')} failed${stderr ? `: ${stderr}`
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/988a709475ab759e.
Report an issue: GitHub.