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

branch_in_use

branch_in_use

Error message

branch_in_use:${plan.branchName}

What it means

Thrown by ensureWorktree when the requested branch name is already checked out in another worktree (git forbids the same branch in two worktrees). The library detects this up front via hasBranchInUse across all known worktrees so the failure is deterministic instead of surfacing as a cryptic git error later.

Source

Thrown at src/team/worktree.ts:441

    if (plan.branchName) {
      upsertCurrentTaskBaseline(plan.repoRoot, {
        branch_name: plan.branchName,
        worktree_path: reused.worktreePath,
        base_ref: plan.baseRef,
        status: 'active',
      });
    }

    return reused;
  }

  if (existsSync(plan.worktreePath)) {
    throw new Error(`worktree_path_conflict:${plan.worktreePath}`);
  }

  if (plan.branchName && hasBranchInUse(allWorktrees, plan.branchName, plan.worktreePath)) {
    throw new Error(`branch_in_use:${plan.branchName}`);
  }

  if (plan.branchName) {
    assertCurrentTaskBranchAvailable(plan.repoRoot, plan.branchName, plan.worktreePath);
  }

  mkdirSync(dirname(plan.worktreePath), { recursive: true });
  const branchAlreadyExisted = plan.branchName ? branchExists(plan.repoRoot, plan.branchName) : false;

  const addArgs = ['worktree', 'add'];
  if (plan.detached) {
    addArgs.push('--detach', plan.worktreePath, plan.baseRef);
  } else if (branchAlreadyExisted) {
    addArgs.push(plan.worktreePath, plan.branchName as string);
  } else {
    addArgs.push('-b', plan.branchName as string, plan.worktreePath, plan.baseRef);
  }

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Pick a unique branch name per worktree (append a run id or slug: `task-<id>` instead of `task`)
  2. Find the holder: `git worktree list` in the repo, then either remove that worktree or reuse it instead of creating a new one
  3. If the holding worktree is stale: `git worktree remove <path>` (or `git worktree prune`) then retry
  4. Pass a falsy branchName if the plan does not need a specific branch, letting git create a detached/new ref

Example fix

// before
ensureWorktree({ repoRoot, branchName: 'agent', worktreePath: '.worktrees/a1' });
ensureWorktree({ repoRoot, branchName: 'agent', worktreePath: '.worktrees/a2' }); // branch_in_use:agent

// after
ensureWorktree({ repoRoot, branchName: 'agent-a1', worktreePath: '.worktrees/a1' });
ensureWorktree({ repoRoot, branchName: 'agent-a2', worktreePath: '.worktrees/a2' });
Defensive patterns

Strategy: validation

Validate before calling

import { execSync } from 'node:child_process';

function branchIsFree(repoRoot: string, branch: string): boolean {
  const list = execSync('git worktree list --porcelain', { cwd: repoRoot, encoding: 'utf8' });
  return !list.split('\n').some((l) => l.startsWith('branch ') && l.trim().endsWith(branch));
}

Try / catch

try {
  ensureWorktree(plan);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('branch_in_use:')) {
    plan.branchName = `${plan.branchName}-${Date.now()}`; // uniquify and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling ensureWorktree with plan.branchName set to a branch that another worktree (other than plan.worktreePath itself) currently has checked out. Typical with shared, non-unique branch names like 'main', 'task', or a per-issue branch reused across parallel benches.

Common situations: Parallel agent benches all defaulting to the same branch name; a previous bench still holding the branch checked out in its worktree; re-running a team start after a partial cleanup that removed the directory but not the git worktree registration.

Related errors


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