can1357/oh-my-pi · error · ToolError

could not find an unused worktree path under ${basePath} (tr

Error message

could not find an unused worktree path under ${basePath} (tried ${WORKTREE_PATH_MAX_SUFFIX} suffixes)

What it means

resolveAvailableWorktreePath probes candidate paths (basePath, basePath-2, ... up to WORKTREE_PATH_MAX_SUFFIX) looking for a non-existent location for the new worktree. If every candidate exists (or stat errors other than ENOENT persist), it throws this ToolError after exhausting all suffixes.

Source

Thrown at packages/coding-agent/src/tools/gh-pr-checkout.ts:113

export async function resolveAvailableWorktreePath(
	basePath: string,
	existingWorktrees: VcsWorktreeEntry[],
): Promise<string> {
	const registered = new Set(existingWorktrees.map(entry => path.resolve(entry.path)));
	for (let attempt = 0; attempt < WORKTREE_PATH_MAX_SUFFIX; attempt += 1) {
		const candidate = attempt === 0 ? basePath : `${basePath}-${attempt + 1}`;
		const normalized = path.resolve(candidate);
		if (registered.has(normalized)) continue;
		try {
			await fs.stat(normalized);
		} catch (error) {
			if (isEnoent(error)) {
				return candidate;
			}
			throw error;
		}
	}
	throw new ToolError(
		`could not find an unused worktree path under ${basePath} (tried ${WORKTREE_PATH_MAX_SUFFIX} suffixes)`,
	);
}

export function selectPrCloneUrl(originUrl: string | undefined, repo: Pick<GhRepoViewData, "url" | "sshUrl">): string {
	if (originUrl?.startsWith("http://") || originUrl?.startsWith("https://")) {
		return normalizeOptionalString(repo.url) ?? normalizeOptionalString(repo.sshUrl) ?? "";
	}

	return normalizeOptionalString(repo.sshUrl) ?? normalizeOptionalString(repo.url) ?? "";
}

export async function getRemoteUrls(repoRoot: string, signal?: AbortSignal): Promise<Map<string, string>> {
	const repo = vcs.requireGit(repoRoot);
	const remotes = await repo.remoteList(signal);
	const urls = new Map<string, string>();
	for (const remoteName of remotes) {
		const remoteUrl = await repo.remoteUrl(remoteName, signal);

View on GitHub (pinned to 9690622007)

Solutions

  1. Clean up stale worktrees: `git worktree list` then `git worktree remove <path>` (or delete leftover directories) and retry.
  2. Free/rename the occupied candidate paths under basePath so a suffix becomes available.
  3. Raise WORKTREE_PATH_MAX_SUFFIX or use a different base directory if this is a high-churn environment.

Example fix

# before: all candidates exist
$ git worktree list  # many stale entries pr-12, pr-12-2, ...
# after
$ git worktree remove ../project-pr-12 && git worktree prune
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from "node:fs";
function worktreePathAvailable(basePath) {
  return !fs.existsSync(basePath) && !fs.existsSync(`${basePath}-2`);
}

Try / catch

try {
  await checkoutPullRequest({ cwd, pr });
} catch (err) {
  if (err instanceof ToolError && err.message.includes("unused worktree path")) {
    // prune stale worktrees then retry once
    await $`git worktree prune`.cwd(cwd);
  } else throw err;
}

Prevention

When it happens

Trigger: checkoutPullRequest called when a project directory accumulates many existing worktrees/dirs with the same base name — e.g. repeatedly checking out PRs that map to the same base path until all suffixed names are taken.

Common situations: Long-lived machine where prior PR checkouts were never cleaned up; leftover directories from crashed runs; a base path collision with an existing project folder name.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d0fbdb98dd76b317. Report an issue: GitHub.