#Convex getUserIdentity sometimes returns null

15 messages · Page 1 of 1 (latest)

steel halo
#

Im not sure if there is a fix in progress for this or not. But in my application there seems to be a race condition where 50% of the time getUserIdentity will return null even though the user is signed in. typically when you refresh the page.

dapper chasmBOT
#

Hi @steel halo. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

This is a known issue when using Convex authentication (including with providers like Clerk or WorkOS): there can be a race condition where, after a page refresh or immediately after sign-in, ctx.auth.getUserIdentity() returns null even though the user is signed in. This typically happens because the Convex client hasn't finished authenticating with the backend when your query runs. The authentication process involves the browser receiving a token and then sending it to Convex over a WebSocket, which can take a moment to complete after a page load or sign-in event.

Key points and workarounds:

#
  • If you call a Convex query (e.g., via useQuery) before authentication is complete, getUserIdentity will return null. This is especially common if your component is not wrapped in Convex's <Authenticated> component, or if you don't check isAuthenticated from useConvexAuth() before making queries.
  • The recommended solution is to ensure that any component making authenticated queries is a child of <Authenticated> from convex/react, or that you only call queries when isAuthenticated is true. This guarantees that the Convex client has finished authenticating before your query runs, preventing the race condition.
  • If you need to handle this at a lower level, you can return a sentinel value (like null) from your query when getUserIdentity is null, and handle the loading state on the client.
  • This is not a bug in Convex per se, but a result of the asynchronous nature of authentication and the need to wait for the client to be fully authenticated before making queries that require authentication.
  • There are ongoing discussions and improvements in the Convex community to make this experience smoother, such as holding off on queries until authentication is complete, but the current best practice is to use <Authenticated> or check isAuthenticated before making queries.

References:

#

In summary:
Wrap your authenticated queries/components in <Authenticated> or check isAuthenticated before making queries. This will prevent the race condition where getUserIdentity returns null even though the user is signed in.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@steel halo Kapa.ai is still learning and improving, please let me know how I did by reacting below.

steel halo
#

@dapper chasm In my application Im preloading the getCurrentUser query on the server side so that I can handle navigation based on the result if needed. Im not sure if there is a fix in progress for this or not. But in my application there seems to be a race condition where 50% of the time getUserIdentity will return null even though the user is signed in. typically when you refresh the page.

import { preloadedQueryResult, preloadQuery } from "convex/nextjs";
import { redirect } from "next/navigation";
import { Sidebar, SidebarContent } from "@/components/ui/sidebar";
import { api } from "@/convex/_generated/api";
import { getToken } from "@/lib/auth-server";
import { AppSidebarFooter } from "./sidebar-footer";
import { SidebarItems } from "./sidebar-items";
import { AppSidebarLogo } from "./sidebar-logo";

export async function AppSidebar() {
    const token = await getToken();

    const preloadedUserQuery = await preloadQuery(
        api.auth.getCurrentUser,
        {},
        { token },
    );

    const user = preloadedQueryResult(preloadedUserQuery);

    if (!user?.onboardingCompleted) {
        redirect("/setup-profile");
    }

    return (
        <Sidebar variant="inset">
            <AppSidebarLogo />
            <SidebarContent>
                <SidebarItems />
            </SidebarContent>
            <AppSidebarFooter preloaded={preloadedUserQuery} />
        </Sidebar>
    );
}
dapper chasmBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Your approach to preloading the getCurrentUser query on the server side using preloadQuery and preloadedQueryResult is correct for handling navigation based on the user's state in a Next.js App Router Server Component. This pattern is supported and recommended in the Convex documentation for server-side authentication and data preloading:

  • You are correctly obtaining the token on the server (with getToken), passing it to preloadQuery, and then extracting the result with preloadedQueryResult.
  • This ensures that the query is executed with the correct authentication context on the server, and you can safely use the result to make navigation decisions (like redirecting if onboarding is not completed).

This pattern is explicitly documented in the Convex docs for Next.js server rendering and server-side authentication:

import { preloadQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export async function TasksWrapper() {
  const token = await getAuthToken();
  const preloadedTasks = await preloadQuery(
    api.tasks.list,
    { list: "default" },
    { token },
  );
  return <Tasks preloadedTasks={preloadedTasks} />;
}

(Convex Next.js Server Rendering: Server-side authentication)

You are also using preloadedQueryResult to synchronously access the result, which is the documented way to get the value from a preloaded query in a Server Component (Convex Next.js Server Rendering).

Summary:
Your implementation follows Convex's best practices for server-side authentication and preloading data in Next.js App Router. As long as getToken returns a valid JWT for the current user, this approach is robust and recommended.

#

If you encounter issues with getUserIdentity returning null, double-check that the token is valid and being passed correctly, as this is the most common cause of such issues in server-side rendering scenarios with Convex [source].

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@steel halo Kapa.ai is still learning and improving, please let me know how I did by reacting below.

steel halo
#

@dapper chasm In my application Im preloading the getCurrentUser query on the server side so that I can handle navigation based on the result if needed. Im not sure if there is a fix in progress for this or not. But in my application there seems to be a race condition where 50% of the time getUserIdentity will return null even though the user is signed in. typically when you refresh the page.

import { preloadedQueryResult, preloadQuery } from "convex/nextjs";
import { redirect } from "next/navigation";
import { Sidebar, SidebarContent } from "@/components/ui/sidebar";
import { api } from "@/convex/_generated/api";
import { getToken } from "@/lib/auth-server";
import { AppSidebarFooter } from "./sidebar-footer";
import { SidebarItems } from "./sidebar-items";
import { AppSidebarLogo } from "./sidebar-logo";

export async function AppSidebar() {
    const token = await getToken();

    const preloadedUserQuery = await preloadQuery(
        api.auth.getCurrentUser,
        {},
        { token },
    );

    const user = preloadedQueryResult(preloadedUserQuery);

    if (!user?.onboardingCompleted) {
        redirect("/setup-profile");
    }

    return (
        <Sidebar variant="inset">
            <AppSidebarLogo />
            <SidebarContent>
                <SidebarItems />
            </SidebarContent>
            <AppSidebarFooter preloaded={preloadedUserQuery} />
        </Sidebar>
    );
}
dapper chasmBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You're using the correct pattern for preloading a Convex query on the server with a token, and this is the recommended approach for authenticated server-side data fetching in Next.js with Convex. However, if you're seeing that getUserIdentity sometimes returns null even though the user is signed in, this almost always points to an issue with the token being passed to preloadQuery.

Key points from the knowledge sources:

  • When using preloadQuery on the server, you must explicitly pass a valid JWT token in the options object. If the token is missing, invalid, or not yet available, ctx.auth.getUserIdentity() will return null in your Convex function, even if the user is signed in on the client [Convex Docs: Server-side authentication].
  • This is not a race condition in Convex itself, but rather a result of the token not being reliably available or valid at the time the server-side code runs. Double-check that getToken() always returns a valid, non-expired JWT for the current user session before calling preloadQuery.
  • If you see this issue only on refresh or intermittently, it may be that your token logic is not always returning the correct value, or there is a timing issue in how the token is generated or fetched [Convex Auth Debugging].
  • There is no mention in the knowledge sources of a "fix in progress" for this specific scenario; the documented solution is to ensure the token is always present and valid when preloading queries on the server.
#

Summary:
If getUserIdentity is returning null 50% of the time on server-side preloads, the most likely cause is that the token passed to preloadQuery is missing or invalid in those cases. Carefully audit your getToken implementation and ensure it always returns a valid JWT for the current user session before calling preloadQuery. This is the documented and supported way to avoid this issue with Convex server-side rendering [Convex Docs: Server-side authentication].

If you continue to see issues after confirming the token is always valid, you may want to log the token and server logs as described in the Convex debugging guide to further diagnose the problem [Convex Auth Debugging].

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: