fix(host-service): classify expected GitHub auth and 404 outcomes as non-500s
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 plainError("No GitHub token available...")the moment any procedure requests an Octokit client (seen ongit.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: thegithub()context factory now throwsTRPCError({ code: "PRECONDITION_FAILED", cause: { kind: "NO_GITHUB_TOKEN" } })with the existing message unchanged. Every procedure that callsctx.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:octokitDirectLookupRowtreats a 404 fromoctokit.pulls.getas "no matching PR" and returnsnull, which the single-target caller already handles gracefully (emptyPullRequestsPage) and which matches how the multi-targetPromise.allSettledpath already filters 404 rejections viaisGithubNotFoundError(status/name check, noinstanceof).
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 throwsTRPCError(PRECONDITION_FAILED, cause{ kind: "NO_GITHUB_TOKEN" }) from@trpc/serverinstead of a generic error.packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts: treat@octokit/restpulls.get404 as “no matching PR” and returnnullvia existingisGithubNotFoundError; caller already handles empty results. No call sites changed.- Client-visible behavior: missing GitHub auth is now a
PRECONDITION_FAILEDtRPC error; single-PR direct lookup may returnnullinstead of throwing. Sentry noise is reduced;github.mergePRhandling is unchanged.