[GH-ISSUE #4172] The get-session api could erase the session token #27172

Closed
opened 2026-04-17 18:01:28 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @normalid-awa on GitHub (Aug 23, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/4172

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Normally sets up a better-auth project
  2. Login

Current vs. Expected behavior

It should return the actual session token data, but it return the follow's

set-cookie
__Secure-better-auth.session_token=; Max-Age=0; Domain=di.dragoncustom.top; Path=/; HttpOnly; Secure; SameSite=Lax
set-cookie
__Secure-better-auth.session_data=; Max-Age=0; Domain=di.dragoncustom.top; Path=/; HttpOnly; Secure; SameSite=Lax
set-cookie
__Secure-better-auth.dont_remember=; Max-Age=0; Domain=di.dragoncustom.top; Path=/; HttpOnly; Secure; SameSite=Lax

What version of Better Auth are you using?

1.3.7

System info

System:
    OS: Windows 10 10.0.19045
    CPU: (6) x64 Intel(R) Core(TM) i5-9600K CPU @ 3.70GHz
    Memory: 2.01 GB / 15.92 GB
  Browsers:
    Edge: Spartan (Hey friend

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

Client

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { reactStartCookies } from "better-auth/react-start";
import {
	emailOTP,
	multiSession,
	username,
	organization,
	openAPI,
} from "better-auth/plugins";
import nodemailer from "nodemailer";
import { mikroOrmAdapter } from "better-auth-mikro-orm";
// import { Pool } from "pg";
import orm from "../database/orm.js";
import { readFile } from "fs/promises";
import path from "path";

let emailVerificationHtmlTemplate: string;
let passwordResetVerificationCodeHtmlTemplate: string;

const transporter = nodemailer.createTransport({
	host: process.env.SMTP_HOST as string,
	port: parseInt(process.env.SMTP_PORT as string),
	secure: false, // true for 465, false for other ports
	auth: {
		user: process.env.SMTP_USER as string,
		pass: process.env.SMTP_PASSWORD as string,
	},
});

export const auth = betterAuth({
	database: mikroOrmAdapter(orm),
	// database: new Pool({
	// 	connectionString: process.env.DATABASE_URL as string,
	// }),
	advanced: {
		ipAddress: {
			ipAddressHeaders: ["x-client-ip", "x-forwarded-for"],
			disableIpTracking: false,
		},
		crossSubDomainCookies: {
			enabled: true,
		},
	},
	emailAndPassword: {
		enabled: true,
		requireEmailVerification: true,
		revokeSessionsOnPasswordReset: true,
	},
	emailVerification: {
		sendOnSignUp: true,
		autoSignInAfterVerification: true,
		sendVerificationEmail: async ({ user, url, token }, request) => {
			if (!emailVerificationHtmlTemplate)
				emailVerificationHtmlTemplate = (
					await readFile(
						path.join(
							process.cwd(),
							"src/email/template/verificationEmailTemplate.html",
						),
					)
				).toString();

			const info = await transporter.sendMail({
				from: process.env.SMTP_EMAIL_VERIFY_FROM as string,
				to: user.email,
				subject: "Verify your email address",
				text: `Click the link to verify your email: ${url}`,
				html: emailVerificationHtmlTemplate.replace(
					"{{verificationLink}}",
					url,
				),
			});
			console.log(info);
		},
	},
	socialProviders: {
		github: {
			clientId: process.env.AUTH_GITHUB_ID as string,
			clientSecret: process.env.AUTH_GITHUB_SECRET as string,
		},
		google: {
			clientId: process.env.AUTH_GOOGLE_ID as string,
			clientSecret: process.env.AUTH_GOOGLE_SECRET as string,
		},
		microsoft: {
			clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID as string,
			clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET as string,
			tenantId: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER as string,
		},
	},
	plugins: [
		username(),
		emailOTP({
			expiresIn: 5 * 60,
			async sendVerificationOTP({ email, otp, type }) {
				if (!emailVerificationHtmlTemplate)
					passwordResetVerificationCodeHtmlTemplate = (
						await readFile(
							path.join(
								process.cwd(),
								"src/email/template/passwordResetVerificationCode.html",
							),
						)
					).toString();

				let subject = "";
				let html = ";";

				switch (type) {
					case "forget-password":
						subject = "Password reset verification code";
						html =
							passwordResetVerificationCodeHtmlTemplate.replace(
								"{{otp}}",
								otp,
							);
						break;
				}

				const info = await transporter.sendMail({
					from: process.env.SMTP_EMAIL_VERIFY_FROM as string,
					to: email,
					subject: subject,
					text: subject,
					html: html,
				});
				console.log(info);
			},
		}),
		multiSession(),
		organization({
			allowUserToCreateOrganization: true,
		}),
		openAPI(),
		reactStartCookies(),
	],
});

Additional context

I'm using tanstack-start+hono+pgsql.
It could successfully get the token but it got erase while it was called get-session api.
This issue still exists even I remove all the plugins

Originally created by @normalid-awa on GitHub (Aug 23, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/4172 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Normally sets up a better-auth project 2. Login ### Current vs. Expected behavior It should return the actual session token data, but it return the follow's ``` set-cookie __Secure-better-auth.session_token=; Max-Age=0; Domain=di.dragoncustom.top; Path=/; HttpOnly; Secure; SameSite=Lax set-cookie __Secure-better-auth.session_data=; Max-Age=0; Domain=di.dragoncustom.top; Path=/; HttpOnly; Secure; SameSite=Lax set-cookie __Secure-better-auth.dont_remember=; Max-Age=0; Domain=di.dragoncustom.top; Path=/; HttpOnly; Secure; SameSite=Lax ``` ### What version of Better Auth are you using? 1.3.7 ### System info ```bash System: OS: Windows 10 10.0.19045 CPU: (6) x64 Intel(R) Core(TM) i5-9600K CPU @ 3.70GHz Memory: 2.01 GB / 15.92 GB Browsers: Edge: Spartan (Hey friend ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { reactStartCookies } from "better-auth/react-start"; import { emailOTP, multiSession, username, organization, openAPI, } from "better-auth/plugins"; import nodemailer from "nodemailer"; import { mikroOrmAdapter } from "better-auth-mikro-orm"; // import { Pool } from "pg"; import orm from "../database/orm.js"; import { readFile } from "fs/promises"; import path from "path"; let emailVerificationHtmlTemplate: string; let passwordResetVerificationCodeHtmlTemplate: string; const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST as string, port: parseInt(process.env.SMTP_PORT as string), secure: false, // true for 465, false for other ports auth: { user: process.env.SMTP_USER as string, pass: process.env.SMTP_PASSWORD as string, }, }); export const auth = betterAuth({ database: mikroOrmAdapter(orm), // database: new Pool({ // connectionString: process.env.DATABASE_URL as string, // }), advanced: { ipAddress: { ipAddressHeaders: ["x-client-ip", "x-forwarded-for"], disableIpTracking: false, }, crossSubDomainCookies: { enabled: true, }, }, emailAndPassword: { enabled: true, requireEmailVerification: true, revokeSessionsOnPasswordReset: true, }, emailVerification: { sendOnSignUp: true, autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url, token }, request) => { if (!emailVerificationHtmlTemplate) emailVerificationHtmlTemplate = ( await readFile( path.join( process.cwd(), "src/email/template/verificationEmailTemplate.html", ), ) ).toString(); const info = await transporter.sendMail({ from: process.env.SMTP_EMAIL_VERIFY_FROM as string, to: user.email, subject: "Verify your email address", text: `Click the link to verify your email: ${url}`, html: emailVerificationHtmlTemplate.replace( "{{verificationLink}}", url, ), }); console.log(info); }, }, socialProviders: { github: { clientId: process.env.AUTH_GITHUB_ID as string, clientSecret: process.env.AUTH_GITHUB_SECRET as string, }, google: { clientId: process.env.AUTH_GOOGLE_ID as string, clientSecret: process.env.AUTH_GOOGLE_SECRET as string, }, microsoft: { clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID as string, clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET as string, tenantId: process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER as string, }, }, plugins: [ username(), emailOTP({ expiresIn: 5 * 60, async sendVerificationOTP({ email, otp, type }) { if (!emailVerificationHtmlTemplate) passwordResetVerificationCodeHtmlTemplate = ( await readFile( path.join( process.cwd(), "src/email/template/passwordResetVerificationCode.html", ), ) ).toString(); let subject = ""; let html = ";"; switch (type) { case "forget-password": subject = "Password reset verification code"; html = passwordResetVerificationCodeHtmlTemplate.replace( "{{otp}}", otp, ); break; } const info = await transporter.sendMail({ from: process.env.SMTP_EMAIL_VERIFY_FROM as string, to: email, subject: subject, text: subject, html: html, }); console.log(info); }, }), multiSession(), organization({ allowUserToCreateOrganization: true, }), openAPI(), reactStartCookies(), ], }); ``` ### Additional context I'm using tanstack-start+hono+pgsql. It could successfully get the token but it got erase while it was called get-session api. This issue still exists even I remove all the plugins
GiteaMirror added the lockedbug labels 2026-04-17 18:01:28 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

Hi @normalid-awa! I'm Dosu and I’m helping the better-auth team.

This is a known issue in Better Auth v1.3.7: the get-session API can clear session cookies (set Max-Age=0) if the session is invalid, revoked, or if there's a mismatch in cookie data and its signature. A bug was identified where different expiration times were generated for the session data and its signature, causing getSession to sporadically return null and erase cookies. This has been fixed in newer versions by ensuring a single expiresAt value is used for both the session data and signature, which prevents unintended cookie clearing [PR #3283].

Another fix ensures session data present in cookies is consistently added to the context, preventing get-session from returning null or clearing cookies erroneously [PR #3578].

To resolve this, upgrade to the latest version of Better Auth. If you need to support cross-domain or subdomain cookies, set advanced.defaultCookieAttributes to { sameSite: "none", secure: true, httpOnly: true } and ensure your frontend requests include credentials and your backend CORS config allows credentials [cookie docs].

If you still see this issue after upgrading, check your cookie handling logic and make sure you use the recommended methods for setting and sending cookies (e.g., authClient.getCookie()), as manual handling can cause validation failures and cookie clearing [related issue].

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3216605134 --> @dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Greeting --> Hi @normalid-awa! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a known issue in Better Auth v1.3.7: the get-session API can clear session cookies (set Max-Age=0) if the session is invalid, revoked, or if there's a mismatch in cookie data and its signature. A bug was identified where different expiration times were generated for the session data and its signature, causing getSession to sporadically return null and erase cookies. This has been fixed in newer versions by ensuring a single expiresAt value is used for both the session data and signature, which prevents unintended cookie clearing [[PR #3283](https://github.com/better-auth/better-auth/pull/3283)]. Another fix ensures session data present in cookies is consistently added to the context, preventing get-session from returning null or clearing cookies erroneously [[PR #3578](https://github.com/better-auth/better-auth/pull/3578)]. To resolve this, upgrade to the latest version of Better Auth. If you need to support cross-domain or subdomain cookies, set `advanced.defaultCookieAttributes` to `{ sameSite: "none", secure: true, httpOnly: true }` and ensure your frontend requests include credentials and your backend CORS config allows credentials [[cookie docs](https://better-auth.com/docs/concepts/cookies)]. If you still see this issue after upgrading, check your cookie handling logic and make sure you use the recommended methods for setting and sending cookies (e.g., `authClient.getCookie()`), as manual handling can cause validation failures and cookie clearing [[related issue](https://github.com/better-auth/better-auth/issues/3892)]. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/2d6f56cf-1fcf-41f0-a31a-591fe7ce3380?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4172)
Author
Owner

@normalid-awa commented on GitHub (Aug 23, 2025):

@dosu

  1. Upgrade better-auth to latest version didn't help, even to the latest beta version 1.3.8-beta3
  2. Set advanced.defaultCookieAttributes to { sameSite: "none", secure: true, httpOnly: true } didn't help
  3. The config and everything was config and code follow the docs
<!-- gh-comment-id:3216617272 --> @normalid-awa commented on GitHub (Aug 23, 2025): @dosu 1. Upgrade better-auth to latest version didn't help, even to the latest beta version 1.3.8-beta3 2. Set advanced.defaultCookieAttributes to { sameSite: "none", secure: true, httpOnly: true } didn't help 3. The config and everything was config and code follow the docs
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

Your config and version are correct, but with tanstack-start and hono, session cookies can still be erased if set-cookie headers from the backend aren’t properly forwarded to the client. In these stacks, middleware often ignores set-cookie headers by default, so session cookies don’t get refreshed and eventually expire or are invalidated, leading to the erasure you’re seeing—even if everything else is set up right.

To fix this, you need to explicitly forward set-cookie headers in your middleware. Here’s a working example for tanstack-start:

import { createMiddleware } from '@tanstack/start';
import { getHeaders, setHeader } from 'vinxi/http';
import { auth } from '../lib/auth.ts';

export const authMiddleware = createMiddleware().server(async ({ next }) => {
  const headers = new Headers(getHeaders() as HeadersInit);
  const response = await auth.api.getSession({ headers, asResponse: true });
  const setCookieHeader = response.headers.get('set-cookie');
  const data = ((await response.json()) || null);

  if (setCookieHeader) {
    setHeader('set-cookie', setCookieHeader);
  }

  return next({ context: { auth: data } });
});

This ensures cookies set by Better Auth are actually sent to the browser and not dropped by your framework’s middleware layer, which is a common pitfall in these integrations [source].

Also, make sure your frontend requests use credentials: 'include' and your backend CORS config allows credentials. If you’re still seeing issues, check your network tab to confirm set-cookie headers are present in responses and cookies are sent back on subsequent requests.

Let me know if you want a checklist or more debugging steps for your specific stack.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3216623796 --> @dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Answer --> Your config and version are correct, but with tanstack-start and hono, session cookies can still be erased if set-cookie headers from the backend aren’t properly forwarded to the client. In these stacks, middleware often ignores set-cookie headers by default, so session cookies don’t get refreshed and eventually expire or are invalidated, leading to the erasure you’re seeing—even if everything else is set up right. To fix this, you need to explicitly forward set-cookie headers in your middleware. Here’s a working example for tanstack-start: ```ts import { createMiddleware } from '@tanstack/start'; import { getHeaders, setHeader } from 'vinxi/http'; import { auth } from '../lib/auth.ts'; export const authMiddleware = createMiddleware().server(async ({ next }) => { const headers = new Headers(getHeaders() as HeadersInit); const response = await auth.api.getSession({ headers, asResponse: true }); const setCookieHeader = response.headers.get('set-cookie'); const data = ((await response.json()) || null); if (setCookieHeader) { setHeader('set-cookie', setCookieHeader); } return next({ context: { auth: data } }); }); ``` This ensures cookies set by Better Auth are actually sent to the browser and not dropped by your framework’s middleware layer, which is a common pitfall in these integrations [[source]](https://github.com/better-auth/better-auth/issues/722#issuecomment-2565987150). Also, make sure your frontend requests use `credentials: 'include'` and your backend CORS config allows credentials. If you’re still seeing issues, check your network tab to confirm set-cookie headers are present in responses and cookies are sent back on subsequent requests. Let me know if you want a checklist or more debugging steps for your specific stack. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/0bb5a3dd-925b-45e2-b3a8-0bd2bafcc263?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4172)
Author
Owner

@normalid-awa commented on GitHub (Aug 23, 2025):

@dosu

The cookie won't block by any lib/framework, it can get pass. The issue happened like this:

  1. Login -> (several redirect) -> better-auth calls /api/auth/callback/github?code=<code>
  2. The above call gets the token successfully and stores in cookies.
  3. Call get-session api then the return header erased the cookie.
<!-- gh-comment-id:3216631840 --> @normalid-awa commented on GitHub (Aug 23, 2025): @dosu The cookie won't block by any lib/framework, it can get pass. The issue happened like this: 1. Login -> (several redirect) -> better-auth calls `/api/auth/callback/github?code=<code>` 2. The above call gets the token successfully and stores in cookies. 3. Call get-session api then the return header erased the cookie.
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

The get-session API will erase session cookies if the session token or session data signature is invalid, expired, or if the secret used for signing does not match the one used for verification. This is enforced for security reasons and happens immediately if there's any mismatch or integrity failure, even if the token was just set by the OAuth callback. Common causes include secret mismatches between environments, inconsistent cookie prefixes, or subtle differences in how cookies are set and read across redirects or domains. Double-check that your secret is identical everywhere Better Auth runs, your cookie prefix is consistent, and your domain/path/secure attributes match your deployment context. If everything looks correct, enable debug logging for both the OAuth callback and get-session endpoints to capture more details about why the session is being invalidated and cookies erased. More info: cookie/session validation logic, cookie config docs.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3216647681 --> @dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Answer --> The get-session API will erase session cookies if the session token or session data signature is invalid, expired, or if the secret used for signing does not match the one used for verification. This is enforced for security reasons and happens immediately if there's any mismatch or integrity failure, even if the token was just set by the OAuth callback. Common causes include secret mismatches between environments, inconsistent cookie prefixes, or subtle differences in how cookies are set and read across redirects or domains. Double-check that your secret is identical everywhere Better Auth runs, your cookie prefix is consistent, and your domain/path/secure attributes match your deployment context. If everything looks correct, enable debug logging for both the OAuth callback and get-session endpoints to capture more details about why the session is being invalidated and cookies erased. More info: [cookie/session validation logic](https://github.com/better-auth/better-auth/blob/577cfb4ab3b16d22f36892c0e843f36abcee80c3/packages/better-auth/src/api/routes/session.ts), [cookie config docs](https://better-auth.com/docs/concepts/cookies). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/c3a3f2fe-3097-4c6d-8bec-ea5cbc9e3f3b?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4172)
Author
Owner

@normalid-awa commented on GitHub (Aug 23, 2025):

@dosu
Still not working with double check. Notice that the orm debug log output this

[query] select "s0".* from "session" as "s0" where "s0"."token" = 'Fkd7qAmpVgBILWOheF1lh4KipUJE4i4A' limit 1 [took 0 ms, 1 result]
[query] select "u0".* from "user" as "u0" where "u0"."id" is null limit 1 [took 1 ms, 0 results]

The second sql shows that better-auth pass a null id to the where clause, lead to no result return, might trigger this issue

<!-- gh-comment-id:3216676059 --> @normalid-awa commented on GitHub (Aug 23, 2025): @dosu Still not working with double check. Notice that the orm debug log output this ``` [query] select "s0".* from "session" as "s0" where "s0"."token" = 'Fkd7qAmpVgBILWOheF1lh4KipUJE4i4A' limit 1 [took 0 ms, 1 result] [query] select "u0".* from "user" as "u0" where "u0"."id" is null limit 1 [took 1 ms, 0 results] ``` The second sql shows that better-auth pass a null id to the where clause, lead to no result return, might trigger this issue
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

The null user id in your ORM query means the session was created without a valid user id, so when get-session runs, it can't find the user and erases the cookies for security reasons. This usually happens if the OAuth provider response or your mapProfileToUser logic doesn't return a valid id, or if the adapter fails to assign one during user creation. The session creation code expects user.id to be present and sets session.userId from it—if it's missing, you get exactly the query you see: where "id" is null [source].

Check your GitHub provider config and any custom mapProfileToUser function to make sure they always return a valid id (string or number). Add debug logs right before session creation to confirm user.id is set. If you use a custom user schema or adapter, make sure your database always generates and returns a valid id for new users. If you’re using the latest version, also confirm your user id types are consistent (recent PRs fixed some type issues, but a null/undefined id will still break things) [source].

If you need help pinpointing where the id is lost, share your mapProfileToUser and any custom user creation logic—this is almost always where the problem starts.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3216679068 --> @dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Answer --> The null user id in your ORM query means the session was created without a valid user id, so when get-session runs, it can't find the user and erases the cookies for security reasons. This usually happens if the OAuth provider response or your mapProfileToUser logic doesn't return a valid id, or if the adapter fails to assign one during user creation. The session creation code expects user.id to be present and sets session.userId from it—if it's missing, you get exactly the query you see: `where "id" is null` [[source](https://github.com/better-auth/better-auth/blob/577cfb4ab3b16d22f36892c0e843f36abcee80c3/packages/better-auth/src/db/internal-adapter.ts)]. Check your GitHub provider config and any custom mapProfileToUser function to make sure they always return a valid id (string or number). Add debug logs right before session creation to confirm user.id is set. If you use a custom user schema or adapter, make sure your database always generates and returns a valid id for new users. If you’re using the latest version, also confirm your user id types are consistent (recent PRs fixed some type issues, but a null/undefined id will still break things) [[source](https://github.com/better-auth/better-auth/pull/3816)]. If you need help pinpointing where the id is lost, share your mapProfileToUser and any custom user creation logic—this is almost always where the problem starts. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/c0addbf6-46bb-4725-8ff8-17826cd4daf0?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4172)
Author
Owner

@normalid-awa commented on GitHub (Aug 23, 2025):

It's find out it's an adapter issue (mikro-orm)

<!-- gh-comment-id:3216784249 --> @normalid-awa commented on GitHub (Aug 23, 2025): It's find out it's an adapter issue (mikro-orm)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#27172