fix(host-service): classify expected GitHub auth and 404 outcomes as non-500s

Merged
SA
3mo ago
+26-9

Problem

Two expected GitHub outcomes escape host-service tRPC procedures as INTERNAL_SERVER_ERROR 500s and file Sentry issues:

  • HOST-SERVICE-1M (6 events, 1.20.2): when a user has no GitHub credentials configured, the shared github() context factory throws a plain Error("No GitHub token available...") the moment any procedure requests an Octokit client (seen on git.getPullRequestThreads). Every PR view without GitHub auth files a 500.
  • HOST-SERVICE-1Q (2 events, 1.20.2): in workspaceCreation.searchPullRequests, an explicit-PR-number lookup for a PR that doesn't exist or isn't accessible rejects with an Octokit HttpError 404 that propagates unclassified.

Root cause

Both are expected domain states (missing precondition / empty result) thrown as untyped errors, so the tRPC Sentry middleware — which reports anything still INTERNAL_SERVER_ERROR at the boundary — correctly treats them as bugs. Per the repo's error contract, the missing translation belongs at the throw sites, never as a Sentry-side filter.

Fix

  • packages/host-service/src/app.ts: the github() context factory now throws TRPCError({ code: "PRECONDITION_FAILED", cause: { kind: "NO_GITHUB_TOKEN" } }) with the existing message unchanged. Every procedure that calls ctx.github() inherits the classification from this one shared site; no call sites touched.
  • packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts: octokitDirectLookupRow treats a 404 from octokit.pulls.get as "no matching PR" and returns null, which the single-target caller already handles gracefully (emptyPullRequestsPage) and which matches how the multi-target Promise.allSettled path already filters 404 rejections via isGithubNotFoundError (status/name check, no instanceof).

Genuine unexpected GitHub failures (5xx, network, rate limits) still propagate and report exactly as before. github.mergePR classification (HOST-SERVICE-1P) is intentionally untouched — open PR #6228 owns github.ts.

Verification

bunx biome check on the two changed files:

Checked 2 files in 12ms. No fixes applied.

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

$ tsc --noEmit --emitDeclarationOnly false
typecheck exit=0

No existing tests cover the changed code paths (no co-located test files for app.ts or search-pull-requests.ts), so no test run applies.

Refs HOST-SERVICE-1M
Refs HOST-SERVICE-1Q

https://claude.ai/code/session_72e9200a-ff85-4667-ac6d-add2c90fb109


Summary by cubic

Classifies two expected GitHub outcomes so they no longer surface as 500s or file Sentry issues. Previously, missing GitHub credentials or a direct PR lookup 404 resulted in INTERNAL_SERVER_ERROR; now missing credentials throw PRECONDITION_FAILED and 404 direct lookups return null (empty result). Unexpected GitHub failures still propagate unchanged.

What changed

  • packages/host-service/src/app.ts: github() now throws TRPCError (PRECONDITION_FAILED, cause { kind: "NO_GITHUB_TOKEN" }) from @trpc/server instead of a generic error.
  • packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts: treat @octokit/rest pulls.get 404 as “no matching PR” and return null via existing isGithubNotFoundError; caller already handles empty results. No call sites changed.
  • Client-visible behavior: missing GitHub auth is now a PRECONDITION_FAILED tRPC error; single-PR direct lookup may return null instead of throwing. Sentry noise is reduced; github.mergePR handling is unchanged.