#Uncaught ConvexError: Unauthenticated at getAuthUser [as getAuthUser]

1 messages · Page 1 of 1 (latest)

winged shore
#

Whenever I call authClient.signOut() from a rendered component that uses a query which in turn calls authComponent.getAuthUser(ctx), I am getting an Uncaught ConvexError: Unauthenticated at getAuthUser [as getAuthUser].

Below is a simplified example of my setup:

// component.ts
export default function Component() {
  const data = useQuery(api.data.get);

  if (!data) {
    return <div>Loading...</div>
  }

  return (
    <>
      {data}
    </>
  )
}

// data.ts
export const get = query({ // is triggered again on authClient.signOut(), not sure why
  handler: async (ctx) => {
    const identity = await authComponent.getAuthUser(ctx); // triggered again on authClient.signOut(), throws error because unauthenticated

    // get and return data
  },
});

Component is wrapped in an <Authenticated> tag. The error is only thrown when signing out, otherwise it works just fine.

"next": "16.0.10"
"better-auth": "1.4.7"
"@convex-dev/better-auth": "^0.10.6"

earnest muralBOT
#

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

#

This behavior matches how Convex auth works today: when you sign out, the client briefly becomes unauthenticated, but your subscribed queries are still running and will re‑run once auth changes. If those queries throw when unauthenticated, you’ll see exactly the kind of error you’re describing.

A few relevant points from the docs and threads:

  • ctx.auth.getUserIdentity() (and similarly authComponent.getAuthUser(ctx)) will return null / throw if the client isn’t authenticated yet or has just become unauthenticated. This is common on initial load or around auth transitions.[Auth debug]
  • Convex recommends either:
    • Wrapping components that call authenticated queries in <Authenticated> / <Unauthenticated> / <AuthLoading>, and/or
    • Making the query itself tolerant of “no auth” (returning null or a sentinel instead of throwing).[Clerk React; AuthKit React]

You’re already using <Authenticated>, but on sign‑out the query is still being re‑run once more with no auth, and getAuthUser throws.

To avoid the error, you have two main options (you can combine them):

1. Make the query “safe” when unauthenticated

Instead of using getAuthUser (which throws), use the “safe” variant from your auth component (for Better Auth this is safeGetAuthUser), which returns null instead of throwing when unauthenticated.[Better Auth thread]

Roughly:

// data.ts
export const get = query({
  handler: async (ctx) => {
    const user = await authComponent.safeGetAuthUser(ctx); // returns null if signed out
#

if (!user) {
// Either:
// - return null and let the client render a logged-out state, or
// - throw your own app-specific error if you really want this to be fatal
return null;
}

// get and return data for this user

},
});


Then in your component:

```tsx
export default function Component() {
  const data = useQuery(api.data.get);

  if (!data) {
    return <div>Loading...</div>;
  }

  return <>{data}</>;
}

This way, when authClient.signOut() runs and the query re‑fires without auth, it just returns null instead of throwing.

2. Skip the query when unauthenticated on the client

You can also gate the query with useConvexAuth() and pass "skip" when not authenticated, so Convex doesn’t even call the backend in that state.[Auth best practices]

import { useConvexAuth, useQuery } from "convex/react";
import { api } from "../convex/_generated/api";

export default function Component() {
  const { isAuthenticated } = useConvexAuth();
  const data = useQuery(api.data.get, isAuthenticated ? {} : "skip");

  if (!isAuthenticated) return <div>Signed out</div>;
  if (!data) return <div>Loading...</div>;

  return <>{data}</>;
}

This pattern is what Convex support recommends in similar “error on logout” situations.[Unauthenticated error]


In practice, the most robust fix for your case is:

  • Switch to authComponent.safeGetAuthUser(ctx) in your query, and
  • Optionally also gate the query with useConvexAuth() + "skip" as above.
#

That will prevent Uncaught ConvexError: Unauthenticated at getAuthUser when authClient.signOut() is called, while still letting you handle the signed‑out state cleanly in the UI.

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