Skip to content

fix(desktop,host-service): classify environmental git failures as typed non-500s - #6260

Merged
saddlepaddle merged 5 commits into
mainfrom
sentry/git-env-classify
Aug 13, 2026
Merged

saddlepaddle merged 5 commits into
mainfrom
sentry/git-env-classify

Conversation

@Kitenite

@Kitenite Kitenite commented Aug 7, 2026 •

Copy link
Copy Markdown
Member

Problem

Environmental git failures — the worktree directory deleted out from under a running git process, macOS folder permissions denying reads (fatal: Unable to read current working directory: Operation not permitted / No such file or directory), or a directory that is not a git repository — propagate out of simple-git as plain GitErrors. The tRPC Sentry middleware reports every INTERNAL_SERVER_ERROR, so these routine lifecycle states report as bugs. DESKTOP-1A alone is ~5,300 events/day and the largest quota driver on the desktop project.

Root cause

The typed classification from #6164/#6199 only covers existsSync pre-checks and the exec-based getStatusNoLock. simple-git's own GitError for these environmental failures maps to nothing:

  • desktop, worker path (changes.getBranches → DESKTOP-HQ): the worker funnel translateRawGitError in git-task-worker.ts only classified "not a git repository"; the cwd-unreadable variants crossed the boundary as raw GitError and missed the domain catches in branches.ts/status.ts.
  • desktop, in-process paths (DESKTOP-1A: changes.commit/push/pull/sync/fetch/createPR, changes.stageFiles/discardChanges/discardFiles/stash via security/git-commands.ts, projects.initGitAndOpen): simple-git runs in the main process with no message classification at all.
  • host-service (git.getStatus → HOST-SERVICE-X/Y): resolve-worktree.ts only has the existsSync pre-check; the worktree can vanish between that check and the git spawn, and permission walls pass the check entirely.

Fix

Classification at throw/capture sites only — no Sentry-side filtering. Existing error messages are preserved verbatim; genuine unexpected git failures keep reporting as 500s.

desktop (workspaces/utils/git-errors.ts, one shared helper pair):

  • classifyEnvironmentalGitError message-checks a caught git error ("Unable to read current working directory" either variant → GitEnvironmentError; "not a git repository" → NotGitRepoError). The worker funnel now uses it, so every worker task crosses the boundary in the domain vocabulary and the existing catches in branches.ts/status.ts translate to the existing typed non-500 TRPCErrors (PRECONDITION_FAILED/{kind:"GIT_ENVIRONMENT"}, BAD_REQUEST/{kind:"NOT_GIT_REPO"}).
  • rethrowEnvironmentalGitError applies the same classification at the in-process boundaries (git-operations.ts procedures, security/git-commands.ts command helpers, staging.ts status helpers, projects.initGitAndOpen), rethrowing the same typed TRPCErrors directly. Domain errors are matched by name (never instanceof) since worker serialization strips prototypes.
  • NotGitRepoError moved from git.ts into git-errors.ts (re-exported from git.ts, so all existing importers and instanceof checks are unchanged) to avoid an import cycle.

host-service (trpc/router/git/utils/classify-git-error.ts, matching the #6199 pattern):

  • rethrowEnvironmentalGitError applied at the git.getStatus boundary: cwd gone → NOT_FOUND/{kind:"WORKTREE_MISSING"}, permission wall → PRECONDITION_FAILED/{kind:"GIT_ENVIRONMENT"}, not a repo → BAD_REQUEST/{kind:"NOT_GIT_REPO"}.

Unit tests cover both helpers (all three message variants, name-checked domain errors, and the no-op path for genuine failures); verified they fail under mutation of the classification.

Verification

bunx biome check <11 changed files>:

Checked 11 files in 15ms. No fixes applied.

cd apps/desktop && bun run typecheck:

$ tsc --noEmit
(exit 0)

cd packages/host-service && bun run typecheck:

$ tsc --noEmit --emitDeclarationOnly false
(exit 0)

bun test (desktop: new git-errors.test.ts + existing git-task-handlers.test.ts; host-service: new classify-git-error.test.ts + existing get-check-job-logs.test.ts):

 19 pass / 0 fail (desktop, 2 files)
 8 pass / 0 fail (host-service, 2 files)

Refs DESKTOP-HQ
Refs DESKTOP-1A
Refs HOST-SERVICE-X
Refs HOST-SERVICE-Y

https://claude.ai/code/session_9a505eb2-10a1-4f40-910d-5ee0d7f2e0b4


Summary by cubic

Classifies environmental simple-git failures as typed non-500 @trpc/server errors in Desktop and Host Service. Previously these routine states returned 500s and were reported to Sentry; now they return BAD_REQUEST, PRECONDITION_FAILED, or NOT_FOUND with cause kinds, while unexpected failures stay 500s. Messages are preserved verbatim.

  • Desktop

    • Adds classifyEnvironmentalGitError and rethrowEnvironmentalGitError; applies via new runGitOperation/runGit wrappers across commit/push/pull/sync/fetch/createPR, staging status reads, and projects.initGitAndOpen.
    • Push path classifies environmental failures before push-specific PRECONDITION_FAILED mapping to preserve cause.kind.
    • Worker funnels raw errors through classifyEnvironmentalGitError; moves NotGitRepoError to git-errors.ts and re-exports. Tests added in git-errors.test.ts; tightens no-main-process-blocking.test.ts after consolidation.
  • Host Service

    • Wraps git.getStatus with rethrowEnvironmentalGitError: CWD gone → NOT_FOUND/{kind:"WORKTREE_MISSING"}; permission wall → PRECONDITION_FAILED/{kind:"GIT_ENVIRONMENT"}; not-a-repo → BAD_REQUEST/{kind:"NOT_GIT_REPO"}. Tests added in classify-git-error.test.ts.

No client migration required; codes and cause.kind are now consistent across worker and in-process paths.

Written for commit 387e021. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Git operations now provide clearer, actionable errors for missing repositories, deleted worktrees, and inaccessible working directories.
    • Commit, push, pull, sync, fetch, branch, staging, reset, discard, stash, and pull request workflows handle environmental failures more reliably while preserving existing behavior.
    • Git status retrieval now validates worktrees consistently.
  • Tests

    • Added coverage for Git error classification across common repository and filesystem failure scenarios.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 44cc8e1c-b942-45c7-8cf4-6cccd409a899

📥 Commits

Reviewing files that changed from the base of the PR and between 33e2241 and 387e021.

📒 Files selected for processing (2)
  • apps/desktop/src/lib/trpc/routers/changes/git-operations.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts
  • apps/desktop/src/lib/trpc/routers/changes/git-operations.ts

📝 Walkthrough

Walkthrough

Git environment failures are classified into typed TRPC errors. Desktop Git commands and operations, the Git worker, host-service status handling, staging, and repository initialization use shared error handling.

Changes

Git environment error handling

Layer / File(s) Summary
Git error classification contracts
apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts, packages/host-service/src/trpc/router/git/utils/classify-git-error.ts, **/*git-errors.test.ts, apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
Classifies repository and unreadable working-directory failures, maps them to typed TRPC errors, preserves unrelated errors, and retains the existing NotGitRepoError export path.
Worker and host-service propagation
apps/desktop/src/main/git-task-worker.ts, packages/host-service/src/trpc/router/git/git.ts
The worker and host-service status route use environmental Git error classification and preserve unknown failures.
Desktop command error boundaries
apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts, apps/desktop/src/lib/trpc/routers/changes/staging.ts, apps/desktop/src/lib/trpc/routers/projects/projects.ts, apps/desktop/src/no-main-process-blocking.test.ts
Branch, file, staging, discard, stash, status, and Git initialization paths rethrow recognized environmental failures. The blocking-call allowlist reflects centralized status retrieval.
Desktop Git operation boundaries
apps/desktop/src/lib/trpc/routers/changes/git-operations.ts
Commit, push, pull, sync, fetch, and createPR use runGitOperation while preserving existing workflows and responses.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: ⚪ Minimal · up to 387e0

This change reclassifies routine environmental Git failures into typed non-500 responses while preserving unexpected failures as server errors. No actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant GitOperation
  participant runGitOperation
  participant rethrowEnvironmentalGitError
  participant TRPCError
  GitOperation->>runGitOperation: Execute Git operation
  runGitOperation->>rethrowEnvironmentalGitError: Handle caught Git failure
  rethrowEnvironmentalGitError->>TRPCError: Map recognized environmental failure
  TRPCError-->>GitOperation: Return typed non-500 error
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title uses conventional commit format and clearly summarizes the main change: classifying environmental Git failures as typed non-500 errors.
Description check ✅ Passed The description explains the problem, root cause, fix, affected paths, and verification results, although it omits the template checklist.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sentry/git-env-classify

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/host-service/src/trpc/router/git/git.ts (1)

197-204: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider extending the wrapper to the other in-process git procedures.

Only getStatus classifies environmental failures. Sibling procedures in this router run simple-git in-process against the same worktree and can produce the same messages. Examples: discardChanges (Line 380), discardAllStaged (Line 405), stageAll (Line 457), getDiff, and renameBranch. Those paths still report a deleted or unreadable worktree as a 500. A small shared helper around the procedure bodies would close the gap without changing behavior for unrelated errors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/host-service/src/trpc/router/git/git.ts` around lines 197 - 204,
Extend the existing rethrowEnvironmentalGitError handling from getStatus to the
sibling in-process git procedures discardChanges, discardAllStaged, stageAll,
getDiff, and renameBranch. Wrap each procedure’s simple-git operation so
environmental worktree failures are classified by the shared helper, while
unrelated errors continue to propagate unchanged.
apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts (1)

27-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Three desktop modules define the same environmental-error wrapper. Each module repeats try { … } catch (error) { rethrowEnvironmentalGitError(error); throw error; } because no shared wrapper is exported next to the classifier. Export one helper from apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts and reuse it, so a future change to the boundary behavior applies everywhere at once.

  • apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts#L27-L36: delete the local runGit definition and import the shared helper under the same local name.
  • apps/desktop/src/lib/trpc/routers/changes/git-operations.ts#L34-L43: delete the local runGitOperation definition and import the shared helper under the same local name.
  • apps/desktop/src/lib/trpc/routers/changes/staging.ts#L25-L34: keep getStatusOrRethrow for the assertRegisteredWorktree and client setup, but replace its inline try/catch with a call to the shared helper around git.status().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts` around
lines 27 - 36, Export a shared generic git-command wrapper from
apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts that applies
rethrowEnvironmentalGitError and preserves the original error, then reuse it
across all sites. In
apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts#L27-L36,
remove runGit and import the shared helper under that name; in
apps/desktop/src/lib/trpc/routers/changes/git-operations.ts#L34-L43, remove
runGitOperation and import the helper under that name; in
apps/desktop/src/lib/trpc/routers/changes/staging.ts#L25-L34, retain
getStatusOrRethrow for setup operations but wrap git.status() with the shared
helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/lib/trpc/routers/changes/git-operations.ts`:
- Around line 119-131: Update the push error handling around runGitOperation and
rethrowEnvironmentalGitError so non-TRPCError failures are classified before any
push-specific wrapping. Preserve existing TRPCError propagation, but let
environmental errors such as missing repositories or inaccessible working
directories produce their classified BAD_REQUEST response with the appropriate
cause.kind.

---

Nitpick comments:
In `@apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts`:
- Around line 27-36: Export a shared generic git-command wrapper from
apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts that applies
rethrowEnvironmentalGitError and preserves the original error, then reuse it
across all sites. In
apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts#L27-L36,
remove runGit and import the shared helper under that name; in
apps/desktop/src/lib/trpc/routers/changes/git-operations.ts#L34-L43, remove
runGitOperation and import the helper under that name; in
apps/desktop/src/lib/trpc/routers/changes/staging.ts#L25-L34, retain
getStatusOrRethrow for setup operations but wrap git.status() with the shared
helper.

In `@packages/host-service/src/trpc/router/git/git.ts`:
- Around line 197-204: Extend the existing rethrowEnvironmentalGitError handling
from getStatus to the sibling in-process git procedures discardChanges,
discardAllStaged, stageAll, getDiff, and renameBranch. Wrap each procedure’s
simple-git operation so environmental worktree failures are classified by the
shared helper, while unrelated errors continue to propagate unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b250b031-4733-4cbf-a7ef-593d6eaac3ff

📥 Commits

Reviewing files that changed from the base of the PR and between ddd3d48 and 2c392f6.

📒 Files selected for processing (11)
  • apps/desktop/src/lib/trpc/routers/changes/git-operations.ts
  • apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts
  • apps/desktop/src/lib/trpc/routers/changes/staging.ts
  • apps/desktop/src/lib/trpc/routers/projects/projects.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git-errors.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/git.ts
  • apps/desktop/src/main/git-task-worker.ts
  • packages/host-service/src/trpc/router/git/git.ts
  • packages/host-service/src/trpc/router/git/utils/classify-git-error.test.ts
  • packages/host-service/src/trpc/router/git/utils/classify-git-error.ts

@github-actions

github-actions Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 11 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/host-service/src/trpc/router/git/utils/classify-git-error.ts">

<violation number="1" location="packages/host-service/src/trpc/router/git/utils/classify-git-error.ts:26">
P1: A worktree removed during the existing-check-to-spawn race still surfaces as a 500 instead of `NOT_FOUND/{kind: "WORKTREE_MISSING"}` because this classifier only recognizes Git’s stderr wording, not the `spawn git ENOENT` failure produced for a missing cwd. The classification needs worktree context or an existence check to distinguish this case from Git itself being unavailable, then map the missing-worktree case to the typed error.</violation>
</file>

<file name="packages/host-service/src/trpc/router/git/git.ts">

<violation number="1" location="packages/host-service/src/trpc/router/git/git.ts:202">
P2: A worktree deleted during status loses its path in the typed `NOT_FOUND` error, making failures across multiple workspaces harder to diagnose. Preserving `worktreePath` in the classifier context/cause would match the existing `resolveWorktreePath` error.</violation>
</file>

<file name="apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts">

<violation number="1" location="apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts:85">
P2: Git-client initialization failures can still surface as 500s because `getGitWithShellPath` runs outside `runGit`; the new classification only covers the subsequent command. Including git-client creation in the same classification boundary would keep these environmental failures as typed non-500 errors.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Client as Desktop Client / Host Client
    participant TRPC as tRPC Router
    participant GitOps as Git Operations (in-process)
    participant GitCmd as security/git-commands.ts
    participant Staging as staging.ts
    participant GitErrors as git-errors.ts / classify-git-error.ts
    participant Worker as Worker Thread
    participant SimpleGit as simple-git
    participant Sentry as Sentry Error Reporter

    Note over Client,Sentry: Desktop: In-Process Git Operations
    
    Client->>TRPC: commit/push/pull/sync/fetch/createPR
    TRPC->>GitOps: git-operations.ts
    GitOps->>GitCmd: gitSwitchBranch/gitCheckoutFiles/gitStageFiles/etc.
    GitCmd->>SimpleGit: raw git command
    alt Environmental failure (cwd gone, permission denied, not a repo)
        SimpleGit-->>GitCmd: GitError
        GitCmd->>GitCmd: runGit() catches error
        GitCmd->>GitErrors: rethrowEnvironmentalGitError(error)
        alt NotGitRepoError by name or message
            GitErrors-->>GitCmd: throw TRPCError(BAD_REQUEST, kind:NOT_GIT_REPO)
        else GitEnvironmentError by name or message
            GitErrors-->>GitCmd: throw TRPCError(PRECONDITION_FAILED, kind:GIT_ENVIRONMENT)
        end
        GitCmd-->>TRPC: typed non-500 TRPCError
        TRPC-->>Client: error response (non-500)
    else Genuine git failure
        SimpleGit-->>GitCmd: GitError
        GitCmd->>GitErrors: rethrowEnvironmentalGitError(error) → no-op
        GitCmd-->>TRPC: original error
        TRPC->>Sentry: INTERNAL_SERVER_ERROR reported
        TRPC-->>Client: 500 error
    else Success
        SimpleGit-->>GitCmd: result
        GitCmd-->>GitOps: success
        GitOps-->>TRPC: success response
        TRPC-->>Client: 200 OK
    end

    Note over Client,Sentry: Desktop: Staging Operations
    
    Client->>TRPC: status/discard/stage operations
    TRPC->>Staging: getUntrackedFilePaths/getStagedNewFilePaths
    Staging->>SimpleGit: git.status()
    alt Environmental failure
        SimpleGit-->>Staging: GitError
        Staging->>GitErrors: rethrowEnvironmentalGitError(error)
        GitErrors-->>Staging: throw typed TRPCError
        Staging-->>TRPC: typed non-500
    else Success
        SimpleGit-->>Staging: status result
        Staging-->>TRPC: file paths
    end

    Note over Client,Sentry: Desktop: Worker Path (branches/status)
    
    Client->>TRPC: getBranches/getStatus
    TRPC->>Worker: executeGitTask(task)
    Worker->>SimpleGit: git operation
    alt Environmental failure
        SimpleGit-->>Worker: GitError
        Worker->>GitErrors: classifyEnvironmentalGitError(error)
        alt NotGitRepoError
            GitErrors-->>Worker: NotGitRepoError
        else GitEnvironmentError
            GitErrors-->>Worker: GitEnvironmentError
        end
        Worker-->>TRPC: serialized domain error
        TRPC->>TRPC: existing branch.ts/status.ts catches
        alt NotGitRepoError
            TRPC-->>Client: TRPCError(BAD_REQUEST, kind:NOT_GIT_REPO)
        else GitEnvironmentError
            TRPC-->>Client: TRPCError(PRECONDITION_FAILED, kind:GIT_ENVIRONMENT)
        end
    end

    Note over Client,Sentry: Desktop: initGitAndOpen
    
    Client->>TRPC: initGitAndOpen(path)
    TRPC->>GitOps: initGitRepo(path)
    GitOps->>SimpleGit: git init
    alt Environmental failure
        SimpleGit-->>GitOps: error
        GitOps->>GitErrors: rethrowEnvironmentalGitError(error)
        GitErrors-->>GitOps: throw typed TRPCError
        GitOps-->>TRPC: typed non-500
    end

    Note over Client,Sentry: Host Service: git.getStatus
    
    Client->>TRPC: git.getStatus(workspaceId)
    TRPC->>TRPC: resolveWorktreePath(ctx, workspaceId)
    TRPC->>Worker: gitStatusSnapshotTask(worktreePath)
    Worker->>SimpleGit: git status
    alt Environmental failure (worktree vanished between check and spawn)
        SimpleGit-->>Worker: GitError
        Worker-->>TRPC: serialized error
        TRPC->>GitErrors: rethrowEnvironmentalGitError(error)
        alt CWD gone (No such file or directory)
            GitErrors-->>TRPC: throw TRPCError(NOT_FOUND, kind:WORKTREE_MISSING)
        else Permission denied (Operation not permitted)
            GitErrors-->>TRPC: throw TRPCError(PRECONDITION_FAILED, kind:GIT_ENVIRONMENT)
        else Not a git repository
            GitErrors-->>TRPC: throw TRPCError(BAD_REQUEST, kind:NOT_GIT_REPO)
        end
        TRPC-->>Client: typed non-500 error
    else Success
        Worker-->>TRPC: status snapshot
        TRPC-->>Client: 200 OK
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

cause: { kind: "NOT_GIT_REPO" },
});
}
if (CWD_GONE_PATTERN.test(error.message)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: A worktree removed during the existing-check-to-spawn race still surfaces as a 500 instead of NOT_FOUND/{kind: "WORKTREE_MISSING"} because this classifier only recognizes Git’s stderr wording, not the spawn git ENOENT failure produced for a missing cwd. The classification needs worktree context or an existence check to distinguish this case from Git itself being unavailable, then map the missing-worktree case to the typed error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/git/utils/classify-git-error.ts, line 26:

<comment>A worktree removed during the existing-check-to-spawn race still surfaces as a 500 instead of `NOT_FOUND/{kind: "WORKTREE_MISSING"}` because this classifier only recognizes Git’s stderr wording, not the `spawn git ENOENT` failure produced for a missing cwd. The classification needs worktree context or an existence check to distinguish this case from Git itself being unavailable, then map the missing-worktree case to the typed error.</comment>

<file context>
@@ -0,0 +1,40 @@
+			cause: { kind: "NOT_GIT_REPO" },
+		});
+	}
+	if (CWD_GONE_PATTERN.test(error.message)) {
+		throw new TRPCError({
+			code: "NOT_FOUND",
</file context>

// arrives as a plain error — classify it by message here. The
// worktree can vanish between resolveWorktreePath's existsSync
// check and the git spawn.
rethrowEnvironmentalGitError(error);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A worktree deleted during status loses its path in the typed NOT_FOUND error, making failures across multiple workspaces harder to diagnose. Preserving worktreePath in the classifier context/cause would match the existing resolveWorktreePath error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/git/git.ts, line 202:

<comment>A worktree deleted during status loses its path in the typed `NOT_FOUND` error, making failures across multiple workspaces harder to diagnose. Preserving `worktreePath` in the classifier context/cause would match the existing `resolveWorktreePath` error.</comment>

<file context>
@@ -157,41 +158,50 @@ export const gitRouter = router({
+				// arrives as a plain error — classify it by message here. The
+				// worktree can vanish between resolveWorktreePath's existsSync
+				// check and the git spawn.
+				rethrowEnvironmentalGitError(error);
+				throw error;
+			}
</file context>

await git.checkout(branch);
} else {
throw switchError;
await runGit(() =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Git-client initialization failures can still surface as 500s because getGitWithShellPath runs outside runGit; the new classification only covers the subsequent command. Including git-client creation in the same classification boundary would keep these environmental failures as typed non-500 errors.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts, line 85:

<comment>Git-client initialization failures can still surface as 500s because `getGitWithShellPath` runs outside `runGit`; the new classification only covers the subsequent command. Including git-client creation in the same classification boundary would keep these environmental failures as typed non-500 errors.</comment>

<file context>
@@ -70,28 +82,30 @@ export async function gitSwitchBranch(
-					await git.checkout(branch);
-				} else {
-					throw switchError;
+	await runGit(() =>
+		runWithPostCheckoutHookTolerance({
+			context: `Switched branch to "${branch}" in ${worktreePath}`,
</file context>

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

# Conflicts:
#	apps/desktop/src/lib/trpc/routers/changes/git-operations.ts

This branch was previously deployed

1 inactive deployment
preview — 387e0218 Deployed Aug 13, 2026 by saddlepaddle via Deploy Marketing #7667
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants