From 2bd60f0e5b3c3749d19f19ea7ccbe2c9c7e0fcd8 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Sun, 19 Jul 2026 16:41:21 +0200 Subject: [PATCH] feat(git): enhance error reporting and auth robustness The commit enhances Git operation reliability by improving how authentication errors are detected and displayed to the user. It standardizes Git output language across different locales and introduces a mechanism to summarize complex raw Git stderr messages, ensuring users receive clear feedback when cloning or interacting with repositories that fail due to credentials. - Standardize git command output using LC_ALL=C for consistent English messaging. - Implement error summarization logic to extract the most relevant message from raw Git stderr. - Update credential dialogs to display summarized and improved authentication failure details. --- .claude/settings.local.json | 4 +++- src-tauri/src/git.rs | 7 +++++++ src/App.svelte | 12 +++++++++--- src/lib/credentials.ts | 14 ++++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f756607..366f6ae 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -116,7 +116,9 @@ "Bash(cp /mnt/data/Development/GitLite/PKGBUILD .)", "Bash(sed -i 's/^pkgrel=1/pkgrel=3/' PKGBUILD)", "Bash(node -e \"const fs=require\\('fs'\\); const version='2026.8.1'; const pkgbuild=fs.readFileSync\\('PKGBUILD','utf8'\\).replace\\(/^pkgver=.*\\\\$/m, 'pkgver='+version\\).replace\\(/^pkgrel=.*\\\\$/m, 'pkgrel=1'\\); fs.writeFileSync\\('PKGBUILD', pkgbuild\\);\")", - "Bash(rm PKGBUILD)" + "Bash(rm PKGBUILD)", + "Bash(git -C /mnt/data/Development/GitLite stash)", + "Bash(git -C /mnt/data/Development/GitLite stash pop)" ] } } diff --git a/src-tauri/src/git.rs b/src-tauri/src/git.rs index c0d13fd..259b768 100644 --- a/src-tauri/src/git.rs +++ b/src-tauri/src/git.rs @@ -300,6 +300,10 @@ static CANCELLABLE_GIT_OUTPUT_COUNTER: AtomicU64 = AtomicU64::new(0); fn git_command() -> Command { let mut command = Command::new("git"); + // Force English output regardless of the system locale, so is_auth_error() + // and other message heuristics keep working (e.g. German git prints + // "Authentifizierung fehlgeschlagen" instead of "Authentication failed"). + command.env("LC_ALL", "C"); #[cfg(windows)] command.creation_flags(CREATE_NO_WINDOW); command @@ -4484,6 +4488,9 @@ fn is_auth_error(details: &str) -> bool { || d.contains(" 401") || d.contains("authorization failed") || d.contains("authentication required") + // Server-side message (e.g. Gitea/Forgejo: "remote: Failed to + // authenticate user"), independent of the local git locale. + || d.contains("failed to authenticate") } // Windows' CreateProcess rejects command lines longer than ~32K chars with diff --git a/src/App.svelte b/src/App.svelte index bcd419a..71fc2d8 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -144,6 +144,7 @@ isCredentialExpired, isAuthError, stripAuthPrefix, + summarizeGitError, } from "./lib/credentials"; import { trackAnalyticsEvent, type AnalyticsEventProperties } from "./lib/analytics"; @@ -1997,7 +1998,10 @@ setCloneDialogError(""); if (fromStore) { if (credentialKey) void credDelete(credentialKey).catch(() => {}); - credDialogError = "Credentials were rejected or have expired. Please sign in again."; + const detail = summarizeGitError(message); + credDialogError = detail + ? `${detail} — please sign in again.` + : "Credentials were rejected or have expired. Please sign in again."; } else { credDialogError = message || "Sign-in is required to clone this repository."; } @@ -2596,8 +2600,10 @@ if (fromStore) { if (auth) { if (key) void credDelete(key).catch(() => {}); - credDialogError = - "Credentials were rejected or have expired. Please sign in again."; + const detail = summarizeGitError(message); + credDialogError = detail + ? `${detail} — please sign in again.` + : "Credentials were rejected or have expired. Please sign in again."; credDialogAction = action; credDialogKey = key; credDialogOpen = true; diff --git a/src/lib/credentials.ts b/src/lib/credentials.ts index 558b867..8eab5e2 100644 --- a/src/lib/credentials.ts +++ b/src/lib/credentials.ts @@ -55,3 +55,17 @@ export function stripAuthPrefix(message: string): string { ? message.slice(AUTH_PREFIX.length).trim() : message; } + +/** + * Condenses raw git stderr to the single most relevant line for the + * credential dialog. Prefers the server's own explanation ("remote: ...") + * over git's generic wrapper ("fatal: Authentication failed for ..."). + */ +export function summarizeGitError(message: string): string { + const lines = message + .split("\n") + .map((line) => line.trim()) + .filter(Boolean); + const remote = lines.find((line) => line.toLowerCase().startsWith("remote:")); + return remote ?? lines[0] ?? ""; +}