gastownhall/beads · error

failed to fingerprint target git directory: %w

Error message

failed to fingerprint target git directory: %w

What it means

bd computes a SHA-256 fingerprint of the target worktree's git directory (walk of every file: name, mode, size, mtime, content) to pin its identity before removal. This wraps any error from that walk — unreadable files/dirs, permission errors, files vanishing mid-walk, symlink read failures.

Source

Thrown at cmd/bd/worktree_cmd.go:1028

	}
	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))
	if err != nil {
		return pinnedWorktreeTarget{}, fmt.Errorf("failed to fingerprint target changes: %w", err)
	}

	return pinnedWorktreeTarget{
		path:                 filepath.Clean(worktree.path),
		pathInfo:             pathInfo,
		gitDir:               gitDir,
		gitDirInfo:           gitDirInfo,
		gitMarkerInfo:        gitMarkerInfo,
		gitDirFingerprint:    gitDirFingerprint,
		gitMarkerFingerprint: gitMarkerFingerprint,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix permissions on the gitdir: `chmod -R u+rwX /repo/.git/worktrees/<name>`
  2. Retry the command — transient I/O races (files appearing/disappearing) often clear on a second run
  3. Stop processes (IDEs, sync tools) that touch the repo, or exclude it from real-time scanning

Example fix

// before
ls -la /repo/.git/worktrees/mywt   # root-owned index.lock
// after
sudo chown -R "$USER" /repo/.git/worktrees/mywt && bd worktree remove mywt
Defensive patterns

Strategy: retry

Validate before calling

// ensure the gitdir is fully readable first
return filepath.Walk(gitDir, func(p string, info os.FileInfo, err error) error {
    if err != nil { return err }
    return nil
})

Try / catch

if err != nil {
    if errors.Is(err, fs.ErrPermission) {
        // fix perms and retry once
    }
    time.Sleep(time.Second)
    err = retryInspect()  // transient walk races often clear
}

Prevention

When it happens

Trigger: `bd worktree remove <name>` or observeRevalidation when `filepath.WalkDir` over the worktree's gitdir fails: permission denied, file deleted concurrently, broken stat on an entry, or an unreadable symlink.

Common situations: Root-owned files inside .git/worktrees/<name>; antivirus/backup tools touching the gitdir during the scan; NFS/network filesystems returning transient stat errors; concurrent git operations deleting metadata mid-run.

Related errors


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