Skip to content

fix(frontend): break redirect loop between root and organization routes - #5645

Open
jog1t wants to merge 1 commit into
mainfrom
kacper/fix-org-redirect-loop
Open

fix(frontend): break redirect loop between root and organization routes#5645
jog1t wants to merge 1 commit into
mainfrom
kacper/fix-org-redirect-loop

Conversation

@jog1t

@jog1t jog1t commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
  • Fix an infinite redirect loop between / and /orgs/$organization that crashed the app with "Maximum update depth exceeded" (Sentry HUB-ZG). The org route bounced back to /, which immediately resolved the same org again.
  • Resolve organizations consistently by id from the membership list in both redirectToOrganization and the org route, instead of looking up by id in one place and by slug in the other.
  • Send users with no resolvable organization to /new-org instead of bouncing them to / or back to /login while already signed in.

Fixes HUB-ZG

@railway-app

railway-app Bot commented Sep 2, 2026

Copy link
Copy Markdown

🚅 Deployed to the actors-pr-5645 environment in rivet-frontend

Service Status Web Updated
frontend-inspector 😴 Sleeping (View Logs) Web Sep 3, 2026 at 12:13 am UTC
kitchen-sink 😴 Sleeping (View Logs) Web Sep 3, 2026 at 12:10 am UTC
frontend-cloud 😴 Sleeping (View Logs) Web Sep 3, 2026 at 12:10 am UTC
ladle ✅ Success (View Logs) Web Sep 3, 2026 at 12:04 am UTC
website ❌ Build Failed (View Logs) Web Sep 3, 2026 at 12:02 am UTC
mcp-hub ✅ Success (View Logs) Web Sep 2, 2026 at 11:25 pm UTC

@claude

claude Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review

Fix looks solid. It closes the two gaps that caused the loop: redirectToOrganization and the org route now both resolve the org's slug from the same membership source, and the /orgs/$organization -> / bounce on error is replaced with a terminal /new-org destination for the specific cases (403/404) where the slug is genuinely unresolvable. /new-org has no beforeLoad that re-resolves an org, so the cycle can't reform.

(Note: this replaces my earlier review comment on this PR, which was posted against the previous commit before orgs.$organization.tsx was reworked to call getFullOrganization directly instead of checking against the cached organization.list() result — most of those earlier notes no longer apply to the current diff.)

Notes / things worth double-checking

  1. frontend/src/routes/_context/orgs.$organization.tsx - only status === 403 || 404 routes to /new-org; any other error (network failure, 500, etc.) now throws a real Error instead of silently redirecting home. That's a good change (it stops swallowing transient errors into a redirect), and it's caught by the router's defaultErrorComponent/Sentry (frontend/src/app.tsx), so it won't crash the app. Just confirm this is the intended UX for a transient failure - the user now sees an error page instead of silently landing back on /.

  2. frontend/src/lib/auth.ts - redirectToOrganization now always calls authClient.organization.list() (previously this was skipped when activeOrganizationId was set, since a single getFullOrganization by id was used instead). If orgs.data comes back undefined (network error) this is indistinguishable from "user has zero orgs," so a transient list-fetch failure will send an existing member down the "create new org" (/new-org) path rather than showing an error. Same shape of issue as point 1 but in a different file, and not surfaced to the user/Sentry at all here since the caller just gets false back.

  3. Minor perf: activeOrganizationId set and valid is now the common case, so this trades one getFullOrganization-by-id call for one organization.list() call (whole membership list vs. a single org). Probably fine, but worth confirming list() stays cheap as org membership grows.

  4. Good catch using org.status (not .statusCode) in the new branch - that matches the better-auth client error shape used elsewhere (isAuthError in frontend/src/lib/utils.ts), which differs from the .statusCode convention used for the generated API client errors elsewhere in this codebase. Easy field to get wrong when copying patterns between the two.

Test coverage

No tests added. Given the repo's ban on vi.mock, unit-testing beforeLoad/authClient-dependent routing logic isn't cheap, but this exact bug class (cross-route redirect loops) is easy to silently reintroduce. Worth a manual pass through: fresh user with zero orgs, user with a stale/deleted activeOrganizationId, a stale /orgs/<deleted-or-forbidden-slug> URL, and a genuine backend error (e.g. 500) on the org lookup to confirm the new error page renders instead of looping.

No security concerns - all redirect targets are internal string literals, no open-redirect risk.

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