From ab45bc063ddb0ab585897cdaaac8560b94226573 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Fri, 10 Apr 2026 17:11:17 +0000 Subject: [PATCH] fix(nexus): auto-recover from unknown company prefix When the URL contains a :companyPrefix segment that doesn't match any fetched company, Layout previously rendered a dead-end NotFoundPage with no recovery link. The sidebar's company switcher depends on selectedCompany being non-null, so users landing on a bogus prefix had no way back without hand-typing a new URL. Replace the NotFoundPage branch with a redirect to the same path under the first available company (selectedCompany ?? companies[0]). If the rest of the path is empty, fall back to /dashboard so the target is guaranteed to exist. The hasUnknownCompanyPrefix condition is already gated on companies.length > 0, so the fallback is reachable; the old NotFoundPage remains as a theoretical safety net if somehow both selectedCompany and companies[0] are null. Triggered by a user session after the embedded-postgres wipe: the browser had a stale localStorage selectedCompanyId and the user hand-typed URLs with guessed prefixes like /ASSISTANT/company/settings. Hitting any invalid prefix stranded them on the 404 with no UI to pick a different company. Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/src/components/Layout.tsx | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/ui/src/components/Layout.tsx b/ui/src/components/Layout.tsx index 28773668..c918dcb5 100644 --- a/ui/src/components/Layout.tsx +++ b/ui/src/components/Layout.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { BookOpen, MessageSquare, Moon, Settings, Sun } from "lucide-react"; -import { Link, Outlet, useLocation, useNavigate, useParams } from "@/lib/router"; +import { Link, Navigate, Outlet, useLocation, useNavigate, useParams } from "@/lib/router"; import { CompanyRail } from "./CompanyRail"; import { Sidebar } from "./Sidebar"; import { InstanceSidebar } from "./InstanceSidebar"; @@ -460,10 +460,31 @@ export function Layout() { )} > {hasUnknownCompanyPrefix ? ( - + // [nexus] Auto-recover from bogus URL prefixes by redirecting + // to the same path under the first available company, instead + // of leaving the user stranded on an "Invite not available" + // style dead end with no way back. hasUnknownCompanyPrefix is + // already gated on companies.length > 0, so the fallback is + // guaranteed to resolve. If no fallback exists for some reason, + // fall through to the old NotFoundPage. + (() => { + const fallbackCompany = selectedCompany ?? companies[0] ?? null; + if (!fallbackCompany) { + return ( + + ); + } + const restOfPath = location.pathname.replace(/^\/[^/]+/, "") || "/dashboard"; + return ( + + ); + })() ) : ( )}