Repository navigation
fix(api-gateway): preserve gRPC error semantics instead of collapsing to HTTP 500 - #2
Open
pranav8764 wants to merge 2 commits into
Open
pranav8764 wants to merge 2 commits into
pranav8764 wants to merge 2 commits into
Conversation
Every gRPC error from the Integration Service collapsed into an identical HTTP 500, so the browser could not distinguish "this installation belongs to a different GitHub App, reconnect it" from "the database is down". A repository added through a stale GitHub App therefore failed silently. Add a shared resolveGRPCError/writeGRPCError pair that maps deliberate gRPC codes onto real HTTP statuses and stable, machine-readable error codes: FailedPrecondition -> 409 INSTALLATION_REVOKED NotFound -> 404 NOT_FOUND PermissionDenied -> 403 FORBIDDEN Unauthenticated -> 401 UNAUTHORIZED InvalidArgument -> 400 INVALID_REQUEST Unavailable -> 503 UPSTREAM_UNAVAILABLE Anything else falls through to each handler's pre-existing default status and error code, so nothing that already worked changes shape — including CreateGitHubInstallSession, whose default stays 502. Mapped codes carry fixed messages of our own. Upstream error text may embed installation tokens, webhook signatures, or raw GitHub response bodies, so err.Error() is never forwarded to the client; the existing structured slog logging is untouched. Applied to CreateGitHubInstallSession, GitHubConfig, GitHubCallback, ListInstallations, ListRepositories, AttachRepository, and GetRepository. This is a mapping only and does not depend on the Integration Service emitting FailedPrecondition yet. Covered by table-driven tests over every mapped code, the default fall-through per handler, wrapped status errors, and non-status errors, each asserting no upstream text reaches the response body. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ListConnectedRepositories backs the dashboard's repository list and is the exact path in the original bug report, but it collapsed every gRPC error into an opaque 502. With the Integration Service now returning codes.FailedPrecondition when an installation belongs to a different GitHub App, that reconnect signal never reached the browser. Route it through the same writeGRPCError mapping as the other handlers, keeping 502 / REPOSITORY_LIST_FAILED as its fall-through default so unclassified failures are unchanged. Covered on both of its routes — workspace-scoped and cross-workspace — across every mapped code and the fall-through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pranav8764
force-pushed
the
fix/gateway-grpc-error-mapping
branch
from
August 2, 2026 14:50
ad7e14d to
a476333
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A repository added through the
archiatros-sreGitHub App never appears in the frontend. One reason the bug stayed invisible: the API gateway collapsed every gRPC error from the Integration Service into an indistinguishable HTTP 500 (or 502), so the browser could not tell "this installation belongs to a different GitHub App, reconnect it" from "the database is down".Change
Adds a shared
resolveGRPCError/writeGRPCErrorpair inservices/api-gateway/internal/handler/integration.gothat maps the gRPC status codes the Integration Service uses deliberately onto real HTTP statuses and stable, machine-readable error codes:FailedPreconditionINSTALLATION_REVOKEDNotFoundNOT_FOUNDPermissionDeniedFORBIDDENUnauthenticatedUNAUTHORIZEDInvalidArgumentINVALID_REQUESTUnavailableUPSTREAM_UNAVAILABLEApplied to eight handlers:
CreateGitHubInstallSession,ListConnectedRepositories,GitHubConfig,GitHubCallback,ListInstallations,ListRepositories,AttachRepository, andGetRepository.ListConnectedRepositoriesis the one that closes the loop on the user's report — it backs the dashboard's repository list, on both its workspace-scoped and cross-workspace routes. Paired with #4, a connection orphaned by GitHub App rotation now reaches the browser as an actionable 409INSTALLATION_REVOKEDinstead of an opaque 502.Notes
CreateGitHubInstallSessionandListConnectedRepositoriesstill default to 502 and the rest still default to 500.codes.FailedPreconditionfor a foreign-App installation.err.Error()is never forwarded to the client. The existing structuredsloglogging at each call site is unchanged.writeError/writeJSONenvelope; no new response shape.UpsertLogProvider(400),ListLogProviders(502),GitHubWebhook(401), andDecideFixApproval(409) keep their hand-picked statuses. They already returnFailedPreconditionfor unrelated conditions ("approval has expired", "provider credential encryption is not configured"), which theINSTALLATION_REVOKEDlabel would misdescribe.Testing
Table-driven tests in
services/api-gateway/internal/handler/integration_test.gocover every mapped gRPC code, the default fall-through for all eight handlers,FailedPrecondition-> 409 across all eight, bothListConnectedRepositoriesroutes, wrapped status errors, and plain non-status errors. Each case also asserts that no upstream text (ghs_secret_token, apostgres://DSN,rpc error) reaches the response body.make testalso runs the Python suites;ai-workerfails locally withNo module named pytest, a pre-existing environment gap unrelated to this change.Browser e2e was intentionally skipped here — #5 owns that flow and only one stack exists on the dev machine.
🤖 Generated with Claude Code