From 5b8ca8a34771da886f0bdd92d05790728b0f5057 Mon Sep 17 00:00:00 2001 From: Santhi Prakash Date: Fri, 21 Aug 2026 04:40:21 +0000 Subject: [PATCH] fix(desktop): classify long-path checkout failures as actionable repo errors projectRepoUnavailableReason returned 'unknown' for git failures that mention 'filename too long' or 'unable to checkout working tree', so the Projects panel fell back to the generic 'Repository unavailable' copy. This is the second reported instance of a fixable local-git problem being flattened into the unknown bucket (#5989, sibling of #5348), and the panel needs a reason it can route to actionable copy. Add a new 'path' reason that matches the Windows MAX_PATH surface, give it copy that points the user at git's core.longpaths / Windows long-path setting, and register a TriangleAlert icon plus the card tooltip status. The core.longpaths injection itself ships in #6095; this PR ships the classification half so the panel can render meaningful text once the config fix lands (or for any user whose core.longpaths is unreachable for other reasons). Fixes #5989 (classification half) Signed-off-by: Santhi Prakash --- .../lib/projectRepoAvailability.test.mjs | 31 +++++++++++++++++++ .../projects/lib/projectRepoAvailability.ts | 9 ++++++ .../src/features/projects/ui/ProjectCards.tsx | 5 +++ .../ui/ProjectRepositoryUnavailableState.tsx | 2 ++ 4 files changed, 47 insertions(+) diff --git a/desktop/src/features/projects/lib/projectRepoAvailability.test.mjs b/desktop/src/features/projects/lib/projectRepoAvailability.test.mjs index aa9661dde88..642ac64b4b1 100644 --- a/desktop/src/features/projects/lib/projectRepoAvailability.test.mjs +++ b/desktop/src/features/projects/lib/projectRepoAvailability.test.mjs @@ -45,6 +45,21 @@ test("classifies branch and network failures", () => { ); }); +test("classifies long-path checkout failures as path errors", () => { + assert.equal( + projectRepoUnavailableReason( + new Error( + "error: unable to create file <...>/a_file_with_a_long_name.md: Filename too long", + ), + ), + "path", + ); + assert.equal( + projectRepoUnavailableReason(new Error("fatal: unable to checkout working tree")), + "path", + ); +}); + test("keeps unmatched failures generic", () => { assert.equal( projectRepoUnavailableReason(new Error("git exited with status 128")), @@ -105,6 +120,14 @@ test("never rewrites non-missing reasons", () => { }), "network", ); + assert.equal( + refineRepoUnavailableReason({ + reason: "path", + repositoryChannelId: "22222222-2222-4222-8222-222222222222", + memberChannelIds: [], + }), + "path", + ); }); test("presents access failures without exposing the relay's masked 404", () => { @@ -114,3 +137,11 @@ test("presents access failures without exposing the relay's masked 404", () => { title: "Repository access restricted", }); }); + +test("presents long-path failures with actionable copy", () => { + assert.deepEqual(projectRepoUnavailablePresentation("path"), { + description: + "The repository contains paths longer than the local filesystem allows (Windows MAX_PATH). Enable long paths in your git config or Windows settings, then retry.", + title: "Paths exceed local limit", + }); +}); diff --git a/desktop/src/features/projects/lib/projectRepoAvailability.ts b/desktop/src/features/projects/lib/projectRepoAvailability.ts index d7911a0827f..6ea19d56d0a 100644 --- a/desktop/src/features/projects/lib/projectRepoAvailability.ts +++ b/desktop/src/features/projects/lib/projectRepoAvailability.ts @@ -4,6 +4,7 @@ export type ProjectRepoUnavailableReason = | "unbound" | "authentication" | "network" + | "path" | "ref" | "unknown"; @@ -42,6 +43,11 @@ const PROJECT_REPO_UNAVAILABLE_PRESENTATIONS: Record< "The Buzz git service could not be reached. Check your connection and try again.", title: "Couldn’t reach repository", }, + path: { + description: + "The repository contains paths longer than the local filesystem allows (Windows MAX_PATH). Enable long paths in your git config or Windows settings, then retry.", + title: "Paths exceed local limit", + }, ref: { description: "The selected branch is advertised by the project but is missing from its git remote.", @@ -100,6 +106,9 @@ export function projectRepoUnavailableReason( ) { return "network"; } + if (/filename too long|unable to checkout working tree/.test(message)) { + return "path"; + } return "unknown"; } diff --git a/desktop/src/features/projects/ui/ProjectCards.tsx b/desktop/src/features/projects/ui/ProjectCards.tsx index b8c3ffd273d..362e2fc1bd9 100644 --- a/desktop/src/features/projects/ui/ProjectCards.tsx +++ b/desktop/src/features/projects/ui/ProjectCards.tsx @@ -306,6 +306,11 @@ function RepositoryUnavailableIndicator({ description: "The Buzz git service could not be reached.", label: "Unreachable", }, + path: { + description: + "The repository contains paths longer than the local filesystem allows.", + label: "Path too long", + }, ref: { description: "The advertised branch is missing from the git remote.", label: "Branch missing", diff --git a/desktop/src/features/projects/ui/ProjectRepositoryUnavailableState.tsx b/desktop/src/features/projects/ui/ProjectRepositoryUnavailableState.tsx index 9fde63b21d0..9e34a37f1b7 100644 --- a/desktop/src/features/projects/ui/ProjectRepositoryUnavailableState.tsx +++ b/desktop/src/features/projects/ui/ProjectRepositoryUnavailableState.tsx @@ -6,6 +6,7 @@ import { LockKeyhole, MessageCircle, RefreshCw, + TriangleAlert, } from "lucide-react"; import { useAppNavigation } from "@/app/navigation/useAppNavigation"; @@ -26,6 +27,7 @@ const UNAVAILABLE_ICONS = { access: LockKeyhole, unbound: LockKeyhole, network: CloudOff, + path: TriangleAlert, ref: GitBranch, unknown: CircleAlert, } satisfies Record;