Use nuxt to develop projects and report warnings: [nuxt] useAsyncData must return a value (it should not be undefined) or the request may be duplicated on the client side. #794

Closed
opened 2026-03-13 08:04:38 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @dxhuii on GitHub (Mar 6, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Nuxt project, normally according to the official document interface, no special processing

Current vs. Expected behavior

WARN [nuxt] useAsyncData must return a value (it should not be undefined) or the request may be duplicated on the client side.

What version of Better Auth are you using?

1.2.3

Provide environment information

- OS windows 11
- Chrome 133.0.6943.142
- node 22.14.0
- nuxt 3.15.4

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

export const auth = betterAuth({
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      const user = ctx.context.newSession?.user
      console.log('after', user?.id)
      if (user) {
        const users = await db.select({ userId: userProfile.userId }).from(userProfile).where(eq(userProfile.userId, user?.id as string)).limit(1)
        if (users.length === 0) {
          await db.insert(userProfile).values({
            userId: user?.id as string,
          })
        }
      }
    }),
  },
  plugins: [
    admin({
      defaultRole: 'user',
      defaultBanExpiresIn: 7 * 24 * 60 * 60,
      defaultBanReason: 'Spamming',
      impersonationSessionDuration: 1 * 24 * 60 * 60,
    }),
    username({
      usernameValidator: (username) => {
        if (username === 'admin') {
          return false
        }
        return true
      },
    }),
  ],
  database: drizzleAdapter(db, {
    provider: 'pg',
  }),
})



import { authClient } from '~~/server/utils/auth-client'

export default defineNuxtRouteMiddleware(async (to) => {
  const lang = useCookie('i18n_redirected')
  const prefix = lang.value === 'en' ? '/' : `/${lang.value}/`
  // Check if the user is navigating to the app route
  const { data: loggedIn } = await authClient.useSession(useFetch)
  const isUserNavigatingToTheApp = to.path.startsWith('/app')
  const isNavigatingToLoginOrRegister = to.path.endsWith('/login') || to.path.endsWith('/register')
  if (import.meta.server)
    return
  if (isUserNavigatingToTheApp && !loggedIn.value) {
    return navigateTo(`${prefix}auth/login`)
  }
  if (isNavigatingToLoginOrRegister && loggedIn.value) {
    return navigateTo(prefix)
  }
})

Additional context

const { data: loggedIn } = await authClient.useSession(useFetch) It should be this sentence. The correct value is not returned as required, and the warning is reported

Originally created by @dxhuii on GitHub (Mar 6, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Nuxt project, normally according to the official document interface, no special processing ### Current vs. Expected behavior WARN [nuxt] useAsyncData must return a value (it should not be undefined) or the request may be duplicated on the client side. ### What version of Better Auth are you using? 1.2.3 ### Provide environment information ```bash - OS windows 11 - Chrome 133.0.6943.142 - node 22.14.0 - nuxt 3.15.4 ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript export const auth = betterAuth({ hooks: { after: createAuthMiddleware(async (ctx) => { const user = ctx.context.newSession?.user console.log('after', user?.id) if (user) { const users = await db.select({ userId: userProfile.userId }).from(userProfile).where(eq(userProfile.userId, user?.id as string)).limit(1) if (users.length === 0) { await db.insert(userProfile).values({ userId: user?.id as string, }) } } }), }, plugins: [ admin({ defaultRole: 'user', defaultBanExpiresIn: 7 * 24 * 60 * 60, defaultBanReason: 'Spamming', impersonationSessionDuration: 1 * 24 * 60 * 60, }), username({ usernameValidator: (username) => { if (username === 'admin') { return false } return true }, }), ], database: drizzleAdapter(db, { provider: 'pg', }), }) import { authClient } from '~~/server/utils/auth-client' export default defineNuxtRouteMiddleware(async (to) => { const lang = useCookie('i18n_redirected') const prefix = lang.value === 'en' ? '/' : `/${lang.value}/` // Check if the user is navigating to the app route const { data: loggedIn } = await authClient.useSession(useFetch) const isUserNavigatingToTheApp = to.path.startsWith('/app') const isNavigatingToLoginOrRegister = to.path.endsWith('/login') || to.path.endsWith('/register') if (import.meta.server) return if (isUserNavigatingToTheApp && !loggedIn.value) { return navigateTo(`${prefix}auth/login`) } if (isNavigatingToLoginOrRegister && loggedIn.value) { return navigateTo(prefix) } }) ``` ### Additional context `const { data: loggedIn } = await authClient.useSession(useFetch)` It should be this sentence. The correct value is not returned as required, and the warning is reported
GiteaMirror added the bug label 2026-03-13 08:04:38 -05:00
Author
Owner

@Gobler commented on GitHub (Mar 13, 2025):

I have the same problem although in a much more trivial case - according to the description on the https://www.better-auth.com/docs/integrations/nuxt#middleware

I create middleware which should secure access to pages for a non-logged-in user (without any additional verification - if the user is logged in he has access if he is not then this access is not available) - unfortunately a warning is reported:

useFetch must return a value (it should not be undefined) or the request may be duplicated on the client side.

My code is identical to the example so once the bug has been resolved (if it will require implementation changes) this example would also need to be updated because so far it appears that the documentation indicates an incorrect solution ;p

@Gobler commented on GitHub (Mar 13, 2025): I have the same problem although in a much more trivial case - according to the description on the https://www.better-auth.com/docs/integrations/nuxt#middleware I create middleware which should secure access to pages for a non-logged-in user (without any additional verification - if the user is logged in he has access if he is not then this access is not available) - unfortunately a warning is reported: `useFetch must return a value (it should not be `undefined`) or the request may be duplicated on the client side. ` My code is identical to the example so once the bug has been resolved (if it will require implementation changes) this example would also need to be updated because so far it appears that the documentation indicates an incorrect solution ;p
Author
Owner

@KazBrekker1 commented on GitHub (Mar 16, 2025):

I have the same issue as @Gobler,
The vanilla SSR usage of useSession is causing the same warning.

@KazBrekker1 commented on GitHub (Mar 16, 2025): I have the same issue as @Gobler, The vanilla SSR usage of useSession is causing the same warning.
Author
Owner

@moshetanzer commented on GitHub (Mar 23, 2025):

Hey,

Please confirm you are only getting this when you log out or you have been logged out?

Also, this is only a dev warn and probably doesnt effect any perf as it is in middleware. @Bekacru did say he would try look into it ❤

@moshetanzer commented on GitHub (Mar 23, 2025): Hey, Please confirm you are only getting this when you log out or you have been logged out? Also, this is only a dev warn and probably doesnt effect any perf as it is in middleware. @Bekacru did say he would try look into it ❤
Author
Owner

@moshetanzer commented on GitHub (Mar 23, 2025):

maybe related to https://github.com/better-auth/better-auth/issues/1245

@moshetanzer commented on GitHub (Mar 23, 2025): maybe related to https://github.com/better-auth/better-auth/issues/1245
Author
Owner

@w3cj commented on GitHub (Mar 25, 2025):

The main issue is that /api/auth/get-session returns an empty body if not logged in. The default behavior of useFetch is to fetch again on the client side if the response was empty. So when SSR is happening and not logged in, we get a fetch on the server AND client.

I got around this with a custom useFetch with an onResponse interceptor that returns an object with null data and null session instead of returning undefined.

import type { UseFetchOptions } from "nuxt/app";

function useAuthFetch<T>(
  url: string | (() => string),
  options?: UseFetchOptions<T>,
) {
  return useFetch(url, {
    ...options,
    $fetch: $fetch.create({
      headers: useRequestHeaders(),
      onResponse(context) {
        if (
          context.request === "/api/auth/get-session"
          && !context.response._data) {
          context.response._data = {
            session: null,
            data: null,
          };
        }
      },
    }),
  });
}

You can then pass this into useSession instead of the default useFetch:

const data = await authClient.useSession(useAuthFetch);

This removes the warning and prevents a client side call to /api/auth/get-session when the page loads if the user is not logged in.

@w3cj commented on GitHub (Mar 25, 2025): The main issue is that `/api/auth/get-session` returns an empty body if not logged in. The default behavior of useFetch is to fetch again on the client side if the response was empty. So when SSR is happening and not logged in, we get a fetch on the server AND client. I got around this with a custom useFetch with an onResponse interceptor that returns an object with null data and null session instead of returning undefined. ```ts import type { UseFetchOptions } from "nuxt/app"; function useAuthFetch<T>( url: string | (() => string), options?: UseFetchOptions<T>, ) { return useFetch(url, { ...options, $fetch: $fetch.create({ headers: useRequestHeaders(), onResponse(context) { if ( context.request === "/api/auth/get-session" && !context.response._data) { context.response._data = { session: null, data: null, }; } }, }), }); } ``` You can then pass this into useSession instead of the default useFetch: ```ts const data = await authClient.useSession(useAuthFetch); ``` This removes the warning and prevents a client side call to `/api/auth/get-session` when the page loads if the user is not logged in.
Author
Owner

@w3cj commented on GitHub (Mar 25, 2025):

Update: I was also able to resolve this with an auth middleware hook. No client side changes are required with this hook in place:

import { createAuthMiddleware } from "better-auth/plugins";

export const auth = betterAuth({
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path === "/get-session") {
        if (!ctx.context.session) {
          return ctx.json({
            session: null,
            user: null,
          });
        }
        return ctx.json(ctx.context.session);
      }
    }),
  },
});

Edit: One thing to note is that with this change you'll need to account for it in back-end middleware now. Instead of just checking for existence of session, you should check for existence of session.session or session.user. For example:

import { auth } from "~/lib/auth";

export default defineEventHandler(async (event) => {
  if (event.path.startsWith("/dashboard")) {
    const session = await auth.api.getSession({
      headers: event.headers,
    });
-    if (!session) {
+    if (!session?.user) {
      await sendRedirect(event, "/", 302);
    }
  }
});
@w3cj commented on GitHub (Mar 25, 2025): Update: I was also able to resolve this with an auth middleware hook. No client side changes are required with this hook in place: ```ts import { createAuthMiddleware } from "better-auth/plugins"; export const auth = betterAuth({ hooks: { after: createAuthMiddleware(async (ctx) => { if (ctx.path === "/get-session") { if (!ctx.context.session) { return ctx.json({ session: null, user: null, }); } return ctx.json(ctx.context.session); } }), }, }); ``` Edit: One thing to note is that with this change you'll need to account for it in back-end middleware now. Instead of just checking for existence of session, you should check for existence of session.session or session.user. For example: ```diff import { auth } from "~/lib/auth"; export default defineEventHandler(async (event) => { if (event.path.startsWith("/dashboard")) { const session = await auth.api.getSession({ headers: event.headers, }); - if (!session) { + if (!session?.user) { await sendRedirect(event, "/", 302); } } }); ```
Author
Owner

@moshetanzer commented on GitHub (Mar 26, 2025):

Hey @Bekacru,

Is there any plan to change the default return to

{
session: null,
user: null
}

for when user is not logged in? Currently as noted above an empty body is returned which causes in some meta frameworks a refetch on client.

@moshetanzer commented on GitHub (Mar 26, 2025): Hey @Bekacru, Is there any plan to change the default return to ``` { session: null, user: null } ``` for when user is not logged in? Currently as noted above an empty body is returned which causes in some meta frameworks a refetch on client.
Author
Owner

@cwandev commented on GitHub (Apr 19, 2025):

I tested Better Auth versions 1.2.5 and above — when not logged in, it returned null instead of an empty body, and only made one server-side request. The warning disappeared as a result.

P.S. None of the patches seem to explicitly fix this issue.

@cwandev commented on GitHub (Apr 19, 2025): I tested Better Auth versions 1.2.5 and above — when not logged in, it returned `null` instead of an empty body, and only made one server-side request. The warning disappeared as a result. P.S. None of the patches seem to explicitly fix this issue.
Author
Owner

@Mateleo commented on GitHub (Apr 19, 2025):

@CharleeWa Use import { createAuthMiddleware } from "better-auth/api"; and not import { createAuthMiddleware } from "better-auth/plugins";

@Mateleo commented on GitHub (Apr 19, 2025): @CharleeWa Use `import { createAuthMiddleware } from "better-auth/api";` and not `import { createAuthMiddleware } from "better-auth/plugins";`
Author
Owner

@2u841r commented on GitHub (May 1, 2025):

CJ's video available here

@2u841r commented on GitHub (May 1, 2025): CJ's video available [here](https://youtu.be/DK93dqmJJYg?t=8520)
Author
Owner
@Bekacru commented on GitHub (May 5, 2025): https://github.com/better-auth/better-auth/issues/2178#issuecomment-2852190937
Author
Owner

@Gobler commented on GitHub (May 6, 2025):

Update: I was also able to resolve this with an auth middleware hook. No client side changes are required with this hook in place:

import { createAuthMiddleware } from "better-auth/plugins";

export const auth = betterAuth({
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path === "/get-session") {
if (!ctx.context.session) {
return ctx.json({
session: null,
user: null,
});
}
return ctx.json(ctx.context.session);
}
}),
},
});
Edit: One thing to note is that with this change you'll need to account for it in back-end middleware now. Instead of just checking for existence of session, you should check for existence of session.session or session.user. For example:

import { auth } from "~/lib/auth";

export default defineEventHandler(async (event) => {
if (event.path.startsWith("/dashboard")) {
const session = await auth.api.getSession({
headers: event.headers,
});

  • if (!session) {
  • if (!session?.user) {
    await sendRedirect(event, "/", 302);
    }
    }
    });

@w3cj Did you have any issues with user login when using this? I added the code you suggested, but unfortunately it’s not working ;(

I do get the token, the cookie is created, I can see the session in the database as well, but the endpoint returns that the session does not exist…

https://github.com/user-attachments/assets/dd241fdc-9d8f-424c-84c7-27340d60b7f8

@Gobler commented on GitHub (May 6, 2025): > Update: I was also able to resolve this with an auth middleware hook. No client side changes are required with this hook in place: > > import { createAuthMiddleware } from "better-auth/plugins"; > > export const auth = betterAuth({ > hooks: { > after: createAuthMiddleware(async (ctx) => { > if (ctx.path === "/get-session") { > if (!ctx.context.session) { > return ctx.json({ > session: null, > user: null, > }); > } > return ctx.json(ctx.context.session); > } > }), > }, > }); > Edit: One thing to note is that with this change you'll need to account for it in back-end middleware now. Instead of just checking for existence of session, you should check for existence of session.session or session.user. For example: > > import { auth } from "~/lib/auth"; > > export default defineEventHandler(async (event) => { > if (event.path.startsWith("/dashboard")) { > const session = await auth.api.getSession({ > headers: event.headers, > }); > - if (!session) { > + if (!session?.user) { > await sendRedirect(event, "/", 302); > } > } > }); @w3cj Did you have any issues with user login when using this? I added the code you suggested, but unfortunately it’s not working ;( I do get the token, the cookie is created, I can see the session in the database as well, but the endpoint returns that the session does not exist… https://github.com/user-attachments/assets/dd241fdc-9d8f-424c-84c7-27340d60b7f8
Author
Owner

@Matteonegridev commented on GitHub (Aug 21, 2025):

Hi is this has been fixed so we don't need the authMiddleware? I'm asking because I get the get-session only on login and logout. When I refresh the page while logged out the network doesn't show me a data fetch from the client.

@Matteonegridev commented on GitHub (Aug 21, 2025): Hi is this has been fixed so we don't need the authMiddleware? I'm asking because I get the get-session only on login and logout. When I refresh the page while logged out the network doesn't show me a data fetch from the client.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#794