From 22dd0ffa8f5793e4c4dc23ddb4ec8f2b97649868 Mon Sep 17 00:00:00 2001 From: Nat Budin Date: Tue, 30 Jun 2026 08:41:53 -0700 Subject: [PATCH] Fix blank page when a new convention attendee completes login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user logged in for the first time on a convention site, the appRootLoader correctly detected a missing profile, ran SetupMyProfile to create one, and redirected to /my_profile/setup. But on that redirect, MyProfileForm.loader ran MyProfileQuery with cache-first and got null back from the stale Apollo cache — the SetupMyProfile mutation result was stored under setupMyProfile.my_profile, leaving Convention.my_profile = null in the normalized cache untouched. A null reference is treated as a complete cache hit, so no network request was made. MyProfileForm.loader returned a 404, triggering the RouteErrorBoundary with a cryptic "[object Response]" message instead of the profile setup form. Fix by adding an update callback to the SetupMyProfile mutate() call. After the mutation succeeds, the callback patches Convention.my_profile in the normalized cache to reference the new UserConProfile entity. Subsequent cache-first queries then find a valid (but incomplete) reference and make a real network fetch for the full profile fields, letting MyProfileForm.loader succeed and the form render normally. Co-Authored-By: Claude Sonnet 4.6 --- app/javascript/AppRouter.tsx | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/javascript/AppRouter.tsx b/app/javascript/AppRouter.tsx index 72513cb5694..d1185fb39f4 100644 --- a/app/javascript/AppRouter.tsx +++ b/app/javascript/AppRouter.tsx @@ -1107,7 +1107,27 @@ const appRootLoader: LoaderFunction = async ({ context, r if (!myProfile) { try { - await client.mutate({ mutation: SetupMyProfileDocument }); + const convention = data.convention; + await client.mutate({ + mutation: SetupMyProfileDocument, + update(cache, result) { + const newProfile = result.data?.setupMyProfile?.my_profile; + if (!newProfile) return; + // After creating the profile, wire up the Convention.my_profile reference + // in the normalized cache so cache-first queries (e.g. MyProfileForm.loader) + // see the new profile instead of the stale null entry. Without this, + // MyProfileQuery returns null from cache even though the profile now exists, + // causing MyProfileForm.loader to return a 404 and show a broken page. + const conventionRef = cache.identify({ __typename: 'Convention', id: convention.id }); + const profileRef = cache.identify({ __typename: 'UserConProfile', id: newProfile.id }); + if (conventionRef && profileRef) { + cache.modify({ + id: conventionRef, + fields: { my_profile: () => ({ __ref: profileRef }) }, + }); + } + }, + }); freshlyCreated = true; } catch { return data;