gastownhall/beads · error
target path is not a real directory: %s
Error message
target path is not a real directory: %s
What it means
inspectWorktreeTarget rejects the target if Lstat shows the path is a symlink or not a plain directory. Removal refuses to operate on symlinked or irregular paths because deleting through a symlink could damage files outside the intended worktree, and identity pinning requires a real directory.
Source
Thrown at cmd/bd/worktree_cmd.go:1009
)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to inspect target cleanliness: %w", err)
}
headOID := strings.TrimSpace(string(headOutput))
if headOID == "" || headOID != worktree.headOID {
return pinnedWorktreeTarget{}, fmt.Errorf(
"target HEAD disagrees with git worktree registry (registry %q, target %q)",
worktree.headOID,
headOID,
)
}
pathInfo, err := os.Lstat(worktree.path)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to pin target directory identity: %w", err)
}
if pathInfo.Mode()&os.ModeSymlink != 0 || !pathInfo.IsDir() {
return pinnedWorktreeTarget{}, fmt.Errorf("target path is not a real directory: %s", worktree.path)
}
gitDirInfo, err := os.Lstat(gitDir)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to pin target git directory identity: %w", err)
}
if gitDirInfo.Mode()&os.ModeSymlink != 0 || !gitDirInfo.IsDir() {
return pinnedWorktreeTarget{}, fmt.Errorf("target git directory is not a real directory: %s", gitDir)
}
gitMarkerPath := filepath.Join(worktree.path, ".git")
gitMarkerInfo, err := os.Lstat(gitMarkerPath)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to pin target git marker identity: %w", err)
}
if gitMarkerInfo.Mode()&os.ModeSymlink != 0 || !gitMarkerInfo.Mode().IsRegular() {
return pinnedWorktreeTarget{}, fmt.Errorf("target git marker is not a regular file: %s", gitMarkerPath)
}
gitDirFingerprint, err := fingerprintWorktreeFilesystem(gitDir)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Replace the symlink with a real directory: remove the link and move the target directory into place, or create the worktree fresh with `git worktree add <real-path>`.
- Register the real (resolved) path with bd and remove via that path.
- If the path is now a file, delete the file and re-add the worktree properly.
- Avoid symlinked worktree locations in your project layout.
Example fix
// before ln -s /mnt/big/worktrees/feature-x /repo/.worktrees/feature-x bd worktree remove feature-x // error: target path is not a real directory // after rm /repo/.worktrees/feature-x mv /mnt/big/worktrees/feature-x /repo/.worktrees/feature-x bd worktree remove feature-x
Defensive patterns
Strategy: validation
Validate before calling
import { lstatSync } from "node:fs";
const st = lstatSync(worktreePath); // lstat: does not follow symlinks
if (st.isSymbolicLink() || !st.isDirectory()) {
throw new Error("path must be a real directory, not a symlink or file");
} Type guard
function isRealDirectory(p) {
try { const st = lstatSync(p); return st.isDirectory() && !st.isSymbolicLink(); }
catch { return false; }
} Prevention
- Never replace worktree directories with symlinks; use `git worktree add` at the real location.
- Register the resolved real path with bd.
- Exclude worktree paths from symlink-creating build tooling.
- Check with lstat (not stat) in scripts before removal.
When it happens
Trigger: The registered worktree path is a symlink pointing elsewhere (someone replaced the directory with a link); the path is a regular file; a filesystem oddity after registry creation such that the path no longer Lstats as a real directory.
Common situations: Developers symlinking worktrees from another disk (e.g. ~/worktrees -> /mnt/big/worktrees); a build tool replacing the directory with a symlink; registry recording a path that later became a file; Windows junctions or macOS weirdness.
Related errors
- %s is a symlink to %s; writing would rewrite the link target
- refusing to write migrated hook %s: %w
- failed to get current directory: %w
- failed to inspect created worktree cleanliness: %w %s
- created worktree is dirty after checkout; refusing to contin
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dc1186c22fdfe015.
Report an issue: GitHub.