Project picker shows duplicate Recent rows for one folder due to path-casing (case-insensitive APFS): /Dev vs /dev

Resolved 💬 3 comments Opened Jun 4, 2026 by khushinpatel1 Closed Jun 4, 2026

Summary

The desktop app's project picker ("Recent" list) shows duplicate rows for the same project — one under /Users/<user>/Dev (real, uppercase D) and a ghost under /Users/<user>/dev (lowercase). On a case-insensitive APFS volume these are the same inode (one folder on disk), so the lowercase row points at a path that does not independently exist.

Environment

  • macOS, case-insensitive APFS (default)
  • Claude desktop app (Electron), project picker / FleetView

Steps to reproduce

  1. On a case-insensitive volume, at some point open/launch a project using a lowercase path (e.g. typed in chat, a shell cd, or a tool result that contains /Users/<user>/dev/foo).
  2. Open the project picker.
  3. The project appears twice: once under …/Dev/foo and once under …/dev/foo.

What I expected

The picker should normalize path casing (or dedupe by resolved inode) so one on-disk folder yields exactly one row.

What actually happens

Two rows per project. Notably:

  • The duplication is per-project and tied to projects that were opened before the casing issue was understood; a newer project only ever opened via the uppercase path shows a single row.
  • The ghost rows are not sourced from ~/.claude.json (cleaning the projects dict there does not remove them) nor from the claude-code-sessions store (all cwd/originCwd values are uppercase). They appear to be persisted in the app's IndexedDB recents store (~/Library/Application Support/Claude/IndexedDB/https_claude.ai_0.indexeddb.*), which is why config-level cleanups never fix it.

Why it's stubborn / data-risk note

On case-insensitive APFS, /Dev and /dev resolve to the same inode. Users (or agents) attempting to "clean up" the lowercase path on disk risk rm -rf-ing the real project folder. Please fix by normalizing/deduping casing in the recents store, not by anything that touches the filesystem. A built-in "these two recents resolve to the same inode, collapse them" pass would prevent both the cosmetic dupes and the footgun.

Suggested fix

  • Canonicalize project paths (resolve real-path + case) before writing to the recents object store, and dedupe existing entries by resolved inode on load.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗