Skip to content

fix(host-service): classify a missing project directory as NOT_FOUND in workspaces.create - #6664

Merged
saddlepaddle merged 1 commit into
mainfrom
sentry/create-repo-missing-notfound
Aug 19, 2026
Merged

saddlepaddle merged 1 commit into
mainfrom
sentry/create-repo-missing-notfound

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Aug 19, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Creating a workspace for a project whose repository folder no longer exists at its registered path (moved or deleted outside the app) fails with simple-git's construct error ("Cannot use simple-git on a directory that does not exist") thrown from the create mutation's ctx.git(localProject.repoPath) call. It escapes as INTERNAL_SERVER_ERROR, so the user sees an opaque internal error instead of an actionable "project directory is gone" message, and every retry is a fresh 500 — the latest events show one user retrying repeatedly within minutes against a condition that is deterministic and permanent until they re-point or restore the folder. 13 events in the last 24h, 233 lifetime, regressed on 1.23.0.

User-visible harm: a user whose project folder was moved/deleted outside Superset gets a raw internal error on every workspace-create attempt, with no hint that the project path is the problem.

Reach: the create path is unchanged on main; this ships in the next host-service/desktop release and reaches every user creating workspaces on it. The condition is a routine lifecycle state, so it will keep firing until classified.

Why code (vs. archiving / filtering): this issue already regressed once — #6495 classified the same construct error on the git.getStatus path, and the issue came back through the uncovered create path. Archiving would just repeat that cycle, and Sentry-side suppression is banned by the repo's classification contract (#6164). The classification helper for exactly this state already exists (requireProjectRepoPath, added in #6316); the create mutation was simply never given it.

Root cause

PR #6316 added requireProjectRepoPath — an ENOENT-safe statSync(..., { throwIfNoEntry: false })?.isDirectory() precheck that throws TRPCError NOT_FOUND with cause: { kind: "PROJECT_DIR_MISSING" } — and applied it to search-branches, list-project-worktrees and adopt, but not to workspaces.create or createEnqueued. The reporting middleware reports on INTERNAL_SERVER_ERROR alone, and createEnqueued drives the real create through a server-side caller whose middleware chain reports the escaping construct error even though the enqueue wrapper catches it afterwards.

Fix

Structural precheck only — no message matcher, matching the shape #6316 and #6634 established:

  • In workspaces.create, resolve the path through requireProjectRepoPath(localProject) once, immediately after requireLocalProject, and use the validated path for everything downstream that consumed localProject.repoPath (ensureMainWorkspace, ctx.git, the worker base-ref fetcher, fetchPrMetadata, both listBranchNames calls, applyGeneratedWorkspaceNames).
  • In createEnqueued's cheap-validation layer, the same one-line guard, so a missing directory rejects synchronously with NOT_FOUND instead of enqueueing a create that is guaranteed to fail and settling with an error event. That layer already throws synchronous non-500s today (BAD_REQUEST for a missing id, PRECONDITION_FAILED for an unregistered project), so this adds no new client contract.

Typed cause: reuses the helper's existing PROJECT_DIR_MISSING as-is; nothing new is invented and no new reader is added.

What still reports, deliberately:

  • A directory deleted between the precheck and the git call still surfaces as a 500 — that race is genuinely unexpected.
  • A permission-walled path (EACCES/EPERM) still throws unclassified and keeps reporting: throwIfNoEntry: false swallows only ENOENT, and the new negative test pins that a permission error is NOT converted to NOT_FOUND (the over-match fix(host-service): let destroy complete when the project repo is gone from disk #6634's merge gate had to fix).

Adjacent sites left alone: workspaces.generateBranchName (and aiRename) also touch project.repoPath outside the create mutation; they have not appeared in this issue and are out of scope.

Verification

New tests written first and confirmed failing before the fix: the two NOT_FOUND tests failed (both paths returned 500s), and the EACCES negative pin passed pre-fix (stays INTERNAL_SERVER_ERROR before and after).

bunx biome check on the three changed files:

Checked 3 files in 17ms. No fixes applied.

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

$ tsc --noEmit --emitDeclarationOnly false

(no errors)

Full bun test test in packages/host-service:

 1158 pass
 8 todo
 0 fail
 2709 expect() calls
Ran 1166 tests across 113 files. [178.84s]

Co-located router suites (bun test src/trpc/router/workspaces src/trpc/router/workspace-creation):

 167 pass
 0 fail
Ran 167 tests across 14 files. [3.37s]

Refs HOST-SERVICE-W

https://claude.ai/code/session_01PWSqv5bU8VzRDHP8Z4iWqi


Summary by cubic

Classifies a missing project repo directory as NOT_FOUND during workspace creation to replace opaque 500s. Previously, workspaces.create and workspaces.createEnqueued threw INTERNAL_SERVER_ERROR from simple-git when the repo path was gone; now both precheck and return NOT_FOUND with cause PROJECT_DIR_MISSING, and the enqueued path rejects before scheduling.

  • Validates the repo path once via requireProjectRepoPath after requireLocalProject in workspaces.create, then uses the validated path for all downstream calls (main workspace ensure, git, base-ref fetcher, PR metadata, branch listing, name application).
  • Adds the same guard to createEnqueued so a missing directory rejects synchronously and is not enqueued.
  • Keeps intentional reporting: a directory deleted after the precheck still surfaces as a 500; permission errors (EACCES/EPERM) remain unclassified and report.
  • Adds integration tests for NOT_FOUND on missing directories in both create paths and a negative pin that permission-walled paths stay INTERNAL_SERVER_ERROR.
  • No API shape or schema changes; clients see deterministic NOT_FOUND instead of 500 on this condition. Addresses the classification gap tracked in HOST-SERVICE-W.

Written for commit f67582a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Workspace creation now validates that the project repository path exists before starting operations.
    • Missing project directories return a clear “not found” error.
    • Permission-related access failures are reported accurately instead of being treated as missing directories.
    • Enqueued workspace creation no longer starts when the project directory is unavailable.

…in workspaces.create

Creating a workspace for a project whose repository folder was moved or
deleted outside the app threw simple-git's construct error from
ctx.git(repoPath) in the create mutation, escaping as a reportable 500 on
every retry. PR #6316 added requireProjectRepoPath for exactly this state
but only applied it to search-branches, list-project-worktrees and adopt;
the create mutation (and createEnqueued's cheap validation) never got it,
which is why HOST-SERVICE-W regressed on the current release.

Resolve the path through requireProjectRepoPath once after
requireLocalProject and use the validated path for everything downstream;
give createEnqueued the same one-line guard so it rejects synchronously
instead of enqueueing a create that is guaranteed to fail. A directory
deleted between check and use still reports (genuine race), and a
permission-walled path (EACCES/EPERM) still reports — the guard converts
only a true ENOENT, pinned by a negative test.

Refs HOST-SERVICE-W

Claude-Session: https://claude.ai/code/session_01PWSqv5bU8VzRDHP8Z4iWqi
@coderabbitai

coderabbitai Bot commented Aug 19, 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: 8759637a-c252-40a8-b1c7-a126304116d9

📥 Commits

Reviewing files that changed from the base of the PR and between 106dd52 and f67582a.

📒 Files selected for processing (3)
  • packages/host-service/src/trpc/router/workspaces/workspaces.ts
  • packages/host-service/test/integration/workspace-create-delete.integration.test.ts
  • packages/host-service/test/integration/workspace-create-enqueued.integration.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

Workspace creation now validates and resolves the project repository path before filesystem and Git operations. Integration tests cover missing directories, permission errors, and prevented background creation after repository removal.

Changes

Workspace repository validation

Layer / File(s) Summary
Repository path validation and propagation
packages/host-service/src/trpc/router/workspaces/workspaces.ts
create and createEnqueued now resolve the validated project repository path. Git, pull request, branch, and AI-renaming operations use this path.
Repository path error coverage
packages/host-service/test/integration/workspace-create-delete.integration.test.ts, packages/host-service/test/integration/workspace-create-enqueued.integration.test.ts
Integration tests verify NOT_FOUND for missing repositories, INTERNAL_SERVER_ERROR for permission denial, and no enqueue settlement after repository removal.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to f6758

Workspace creation now returns an actionable NOT_FOUND error for missing project directories and avoids enqueueing guaranteed failures; the change is localized and verified by the supplied checks, with no actionable merge-blocking risk remaining.

Possibly related PRs

Suggested reviewers: kitenite

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses conventional commit format and clearly describes the primary change: classifying missing project directories as NOT_FOUND.
Description check ✅ Passed The description clearly covers the problem, root cause, fix, scope, and verification results, although it omits the template checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sentry/create-repo-missing-notfound

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.

@github-actions

github-actions Bot commented Aug 19, 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! 🎉

@saddlepaddle
saddlepaddle merged commit 2cdf990 into main Aug 19, 2026
20 checks passed
@saddlepaddle
saddlepaddle deleted the sentry/create-repo-missing-notfound branch August 22, 2026 22:25

This branch was previously deployed

1 inactive deployment
preview — f67582ad Deployed Aug 19, 2026 by saddlepaddle via Deploy Admin #8116
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.

1 participant