gastownhall/beads · error

target git directory is not a real directory: %s

Error message

target git directory is not a real directory: %s

What it means

Before removing a worktree, bd pins the filesystem identity of the target's git directory (the worktree's private .git dir under .git/worktrees/). This error means `os.Lstat` on that directory succeeded, but it is a symlink or not a directory. bd refuses to fingerprint/remove a target whose gitdir is not a plain directory, because identity pinning and hashing would be unreliable.

Source

Thrown at cmd/bd/worktree_cmd.go:1016

		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 {
		return pinnedWorktreeTarget{}, fmt.Errorf("failed to fingerprint target git directory: %w", err)
	}
	gitMarkerFingerprint, err := fingerprintWorktreeFilesystem(gitMarkerPath)
	if err != nil {
		return pinnedWorktreeTarget{}, fmt.Errorf("failed to fingerprint target git marker: %w", err)
	}
	statusFingerprint, err := fingerprintWorktreeStatusPaths(worktree.path, string(statusOutput))

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the path in the error with `ls -la`; if it is a symlink, replace it with a real directory or recreate the worktree
  2. Run `git worktree list` and `git worktree prune`, then re-add the worktree with `git worktree add`
  3. If the gitdir was replaced by a file, remove and re-create the worktree cleanly instead of editing internals

Example fix

// before: manual symlinked gitdir
ln -s /elsewhere/wt.git /repo/.git/worktrees/mywt
// after: let git manage it
git worktree remove --force mywt && git worktree add ../mywt -b mybranch
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Lstat(gitDir)
if err != nil || info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
    return fmt.Errorf("refusing: gitdir %s is not a real directory", gitDir)
}

Type guard

func isRealDir(path string) bool {
    info, err := os.Lstat(path)
    return err == nil && info.Mode()&os.ModeSymlink == 0 && info.IsDir()
}

Prevention

When it happens

Trigger: Running `bd worktree remove` (prepareWorktreeRemoval) or a revalidation (observeRevalidation) when the resolved gitdir path is a symlink, was replaced by a file, or the worktree registry points at a stale/corrupted gitdir path.

Common situations: Moving the repository with symlinks in .git/worktrees; manual edits to .git/worktrees/<name>/gitdir; restoring a repo from backup that turned gitdirs into symlinks; tools that rewrite worktree metadata.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/cd727ba3a4773345. Report an issue: GitHub.