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;