Skip to content

fix(frontend): show why a repository never appeared, not an empty dropdown - #5

Open
pranav8764 wants to merge 1 commit into
Golden-Incodent-Corpusfrom
worktree-agent-a181a339973a3261e
Open

pranav8764 wants to merge 1 commit into
Golden-Incodent-Corpusfrom
worktree-agent-a181a339973a3261e

Conversation

@pranav8764

Copy link
Copy Markdown
Owner

Problem

A repository connected through the archiatros-sre GitHub App never appeared, and the user got no indication why.

The backend was returning HTTP 500 — installation 142620052 was created under a previous GitHub App and can no longer mint a token:

"failed to generate installation token","error":"unexpected status code fetching token: 404","installation_id":142620052
ListRepositories ... error: rpc error: code = Internal desc = failed to authorize with github

The UI swallowed all of it:

Failure What the user saw
listGitHubRepositories 500s setRepositories([]) → an empty, enabled dropdown showing only -- Select Repository --, indistinguishable from "this installation genuinely has no repos"
listGitHubInstallations fails .catch(… return { installations: [] }) → the "no installations" branch → an "Install App" button telling the user to do the thing they had just done
Install completes Gateway 303s to ?github=connected, but nothing read the param and there was no polling — the repo only appeared on a later full reload

Change

lib/api.ts — fetchAPI now throws an ApiError carrying the HTTP status and the gateway's machine-readable code. It still extends Error, so the ~17 callers that only read .message are untouched; the signature is unchanged. The gateway's envelope is {"error": CODE, "message": …}, so code ?? error is read (verified over the wire). Adds describeRequestError, settle, isInstallationUsable, and repositoryConnectionHealth.

dashboard/[workspaceId]/page.tsx

  • repoError / installationsError state; both silent swallows replaced with a visible role="alert" + Retry, matching the treatment ConnectedRepositories already used.
  • A failed installations call can never render "Install App" — the error branch is checked before the empty branch.
  • A failed config call no longer reads as "backend not configured" (same bug class, three lines away).
  • Reads ?github=connected via useSearchParams, refetches once, shows a confirmation, then clears the marker. Wrapped in <Suspense> — without it next build fails the route.
  • status === 'revoked' renders as "Reconnect required" with a reconnect action and is excluded from the picker. Missing/unrecognised status still reads as connected, so this degrades gracefully until the server-side status lands.
  • 409 INSTALLATION_REVOKED gets plain-language copy; any other non-2xx still yields a specific error (e.g. failed to list repositories (HTTP 500 LIST_REPOS_FAILED)) rather than an empty dropdown.

dashboard/page.tsx — revoked connections show "Reconnect required" + a Reconnect link instead of a stale "Indexed" badge; leftover raw zinc/red colors migrated to theme tokens.

Bugs found and fixed during review

  • Refresh silently discarded a pending repository choice while leaving the project name filled. handleCreate then skipped the attach with no error, producing a project labelled "No Repository". The choice is now preserved unless GitHub stopped offering that repository.
  • Concurrent fetches could land out of order — an older snapshot could win. Both fetch paths are sequence-guarded.
  • A null JSON error body threw TypeError instead of ApiError, defeating every isApiError branch.
  • A ProjectCard reconnect failure was written to state that rendered nowhere.
  • Submit stayed enabled while repositories were reloading, allowing a cross-installation attach.

Verification

  • npm test — 26 pass (16 new). The 500 case renders the real page component and asserts the error, the Retry, and the absence of -- Select Repository --.
  • npm run lint clean on changed files; npx tsc --noEmit clean; npm run build succeeds.
  • Backend behaviour confirmed live: make health 8/8, the 500 reproduced in the integration-service log, and the error envelope shape confirmed over the wire.

Not yet observable in production

Three behaviours depend on sibling units and are inert until those land — by design, and the code degrades gracefully meanwhile:

  • repository_connections.status is still CHECK (status IN ('connected','disabled','archived','error')) — 'revoked' needs the migration.
  • Nothing writes github_installations.status yet (no installation.deleted webhook handler).
  • No handler emits INSTALLATION_REVOKED yet.

Blocker (outside this repo, needs the user)

The GitHub App archiatros-sre has a Setup URL of http://localhost:8080/... but the gateway publishes on 8180. Every row in github_install_sessions still has consumed_at IS NULL, so no install has ever completed. Until that is corrected at https://github.com/settings/apps/archiatros-sre, a fresh install cannot be exercised.

🤖 Generated with Claude Code

…pdown

A repository connected through the GitHub App never showed up and the UI
gave no reason. The backend was returning 500 (an installation created
under a previous GitHub App can no longer mint a token), but every failure
was swallowed:

- the repository-list catch called setRepositories([]), so a 500 rendered
  as an empty, enabled dropdown showing only "-- Select Repository --",
  indistinguishable from "this installation genuinely has no repos";
- the installations catch returned { installations: [] }, routing the UI
  into the "no installations" branch and rendering an "Install App" button
  that told the user to do the thing they had just done;
- nothing read the ?github=connected marker the gateway redirects back
  with, so a completed install only showed up on a later full reload.

fetchAPI now throws an ApiError carrying the HTTP status and the gateway's
machine-readable code. It still extends Error, so the ~17 callers that only
read .message are unaffected. Both swallows are replaced with a visible
alert plus Retry, matching the treatment ConnectedRepositories already
used. A failed installations call can no longer render "Install App", and
a failed config call no longer reads as "backend not configured".

Repository connections with status 'revoked' render as "Reconnect
required" with a reconnect action and are kept out of the picker; a
missing or unrecognised status still reads as connected, so this degrades
gracefully until the server-side status lands. 409 INSTALLATION_REVOKED
gets plain-language copy, and any other non-2xx still produces a specific
error rather than an empty dropdown.

Also fixed while here: a refresh silently cleared a pending repository
choice while leaving the project name filled, so "Create & Connect
Project" would create a project with no repository and no warning; the
choice is now preserved unless GitHub stopped offering that repository.
Concurrent fetches are sequence-guarded so an older snapshot cannot land
last and win.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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