From 414902136b00c3db36cc3ee5f30d9d357fbbc1a7 Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Thu, 7 May 2026 15:23:44 -0700 Subject: [PATCH] Fix onboarding relay membership denial handling --- .../onboarding/ui/MembershipDenied.tsx | 12 ++++++- .../features/onboarding/ui/OnboardingFlow.tsx | 31 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/desktop/src/features/onboarding/ui/MembershipDenied.tsx b/desktop/src/features/onboarding/ui/MembershipDenied.tsx index df81fa9f4..bdb4c8b3e 100644 --- a/desktop/src/features/onboarding/ui/MembershipDenied.tsx +++ b/desktop/src/features/onboarding/ui/MembershipDenied.tsx @@ -16,7 +16,17 @@ export function MembershipDenied({ onRetry, pubkey, }: MembershipDeniedProps) { - const npub = React.useMemo(() => pubkeyToNpub(pubkey), [pubkey]); + const npub = React.useMemo(() => { + if (!pubkey) { + return "Unknown public key"; + } + + try { + return pubkeyToNpub(pubkey); + } catch { + return pubkey; + } + }, [pubkey]); const [copied, setCopied] = React.useState(false); const handleCopy = React.useCallback(async () => { diff --git a/desktop/src/features/onboarding/ui/OnboardingFlow.tsx b/desktop/src/features/onboarding/ui/OnboardingFlow.tsx index 6ba2c55e3..210b3a6a1 100644 --- a/desktop/src/features/onboarding/ui/OnboardingFlow.tsx +++ b/desktop/src/features/onboarding/ui/OnboardingFlow.tsx @@ -35,15 +35,25 @@ import type { * Returns `true` if denied, `false` if the user is a member (or if the * relay doesn't enforce membership / isn't reachable). */ +function isRelayMembershipDeniedError(error: unknown): boolean { + if (!(error instanceof Error)) { + return false; + } + + return ( + error.message.includes("You must be a relay member") || + error.message.includes("relay_membership_required") || + error.message.includes("restricted: not a relay member") || + error.message.includes("invalid: you are not a relay member") + ); +} + async function checkMembershipDenied(): Promise { try { const { membership, snapshotFound } = await getMyRelayMembershipLookup(); return snapshotFound && membership === null; } catch (error) { - if ( - error instanceof Error && - error.message.includes("relay returned 403") - ) { + if (isRelayMembershipDeniedError(error)) { return true; } // Network errors, 401s, 500s — not membership denials. @@ -271,7 +281,18 @@ export function OnboardingFlow({ if (Object.keys(updatePayload).length > 0) { try { await profileUpdateMutation.mutateAsync(updatePayload); - } catch { + } catch (error) { + if (isRelayMembershipDeniedError(error)) { + try { + const identity = await getIdentity(); + setDeniedPubkey(identity.pubkey); + } catch { + setDeniedPubkey(""); + } + setCurrentPage("membership-denied"); + return; + } + // Error falls through to the error banner / recovery buttons. return; }