{"record":{"id":"dc1186c22fdfe015","repo":"gastownhall/beads","slug":"target-path-is-not-a-real-directory-s","errorCode":null,"errorMessage":"target path is not a real directory: %s","messagePattern":"target path is not a real directory: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/worktree_cmd.go","lineNumber":1009,"sourceCode":"\t)\n\tif err != nil {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"failed to inspect target cleanliness: %w\", err)\n\t}\n\n\theadOID := strings.TrimSpace(string(headOutput))\n\tif headOID == \"\" || headOID != worktree.headOID {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\n\t\t\t\"target HEAD disagrees with git worktree registry (registry %q, target %q)\",\n\t\t\tworktree.headOID,\n\t\t\theadOID,\n\t\t)\n\t}\n\tpathInfo, err := os.Lstat(worktree.path)\n\tif err != nil {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"failed to pin target directory identity: %w\", err)\n\t}\n\tif pathInfo.Mode()&os.ModeSymlink != 0 || !pathInfo.IsDir() {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"target path is not a real directory: %s\", worktree.path)\n\t}\n\tgitDirInfo, err := os.Lstat(gitDir)\n\tif err != nil {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"failed to pin target git directory identity: %w\", err)\n\t}\n\tif gitDirInfo.Mode()&os.ModeSymlink != 0 || !gitDirInfo.IsDir() {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"target git directory is not a real directory: %s\", gitDir)\n\t}\n\tgitMarkerPath := filepath.Join(worktree.path, \".git\")\n\tgitMarkerInfo, err := os.Lstat(gitMarkerPath)\n\tif err != nil {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"failed to pin target git marker identity: %w\", err)\n\t}\n\tif gitMarkerInfo.Mode()&os.ModeSymlink != 0 || !gitMarkerInfo.Mode().IsRegular() {\n\t\treturn pinnedWorktreeTarget{}, fmt.Errorf(\"target git marker is not a regular file: %s\", gitMarkerPath)\n\t}\n\tgitDirFingerprint, err := fingerprintWorktreeFilesystem(gitDir)\n\tif err != nil {","sourceCodeStart":991,"sourceCodeEnd":1027,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/worktree_cmd.go#L991-L1027","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nln -s /mnt/big/worktrees/feature-x /repo/.worktrees/feature-x\nbd worktree remove feature-x\n// error: target path is not a real directory\n// after\nrm /repo/.worktrees/feature-x\nmv /mnt/big/worktrees/feature-x /repo/.worktrees/feature-x\nbd worktree remove feature-x","handlingStrategy":"validation","validationCode":"import { lstatSync } from \"node:fs\";\nconst st = lstatSync(worktreePath); // lstat: does not follow symlinks\nif (st.isSymbolicLink() || !st.isDirectory()) {\n  throw new Error(\"path must be a real directory, not a symlink or file\");\n}","typeGuard":"function isRealDirectory(p) {\n  try { const st = lstatSync(p); return st.isDirectory() && !st.isSymbolicLink(); }\n  catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["worktree","symlink","filesystem-safety"],"backgroundTag":"symlinked-path-rejected","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}