[GH-ISSUE #8125] SvelteKit integration forces secret definition on build time #11003

Open
opened 2026-04-13 07:22:50 -05:00 by GiteaMirror · 18 comments
Owner

Originally created by @megla-hburdow on GitHub (Feb 24, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8125

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

You can use the sveltekit template. The issue is present there:

  1. npx sv create example-project
  2. select better-auth in the stepper
  3. remove the .env file
  4. pnpm run build

Current vs. Expected behavior

Based on the auth.ts file under src/lib/server/auth.ts. I would expact, that the secret gets dynamicly set on runtime.

(see "Auth config" section for reference...)
In the Auth config it uses the import { env } from '$env/dynamic/private'; import which should get the env var on runitme. Because of that I would expect, that I can set the env var on runtime and not on build time.

The build seems to already require the BETTER_AUTH_SECRET in the build context and fails with the following error if not present:

2026-02-24T12:36:51.160Z WARN [Better Auth]: [better-auth] Base URL could not be determined. Please set a valid base URL using the baseURL config option or the BETTER_AUTH_BASE_URL environment variable. Without this, callbacks and redirects may not work correctly.

node:internal/event_target:1122
  process.nextTick(() => { throw err; });
                           ^
[Error [BetterAuthError]: You are using the default secret. Please set `BETTER_AUTH_SECRET` in your environment variables or pass `secret` in your auth config.]

Node.js v24.13.0
 ELIFECYCLE  Command failed with exit code 1.

What version of Better Auth are you using?

1.4.18

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 11 Pro",
    "release": "10.0.26200",
    "cpuCount": 20,
    "cpuModel": "13th Gen Intel(R) Core(TM) i7-13800H",
    "totalMemory": "31.66 GB",
    "freeMemory": "8.38 GB"
  },
  "node": {
    "version": "v24.13.0",
    "env": "development"
  },
  "packageManager": {
    "name": "pnpm",
    "version": "10.30.2"
  },
  "frameworks": [
    {
      "name": "svelte",
      "version": "^5.51.0"
    },
    {
      "name": "@sveltejs/kit",
      "version": "^2.50.2"
    }
  ],
  "databases": [
    {
      "name": "@libsql/client",
      "version": "^0.17.0"
    },
    {
      "name": "drizzle",
      "version": "^0.45.1"
    }
  ],
  "betterAuth": {
    "version": "Unknown",
    "config": null,
    "error": "DATABASE_URL is not set"
  }
}

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

Backend

Auth config (if applicable)

import { betterAuth } from 'better-auth/minimal';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { sveltekitCookies } from 'better-auth/svelte-kit';
import { env } from '$env/dynamic/private';
import { getRequestEvent } from '$app/server';
import { db } from '$lib/server/db';

export const auth = betterAuth({
	baseURL: env.ORIGIN,
	secret: env.BETTER_AUTH_SECRET,
	database: drizzleAdapter(db, { provider: 'sqlite' }),
	emailAndPassword: { enabled: true },
	socialProviders: {
		github: {
			clientId: env.GITHUB_CLIENT_ID,
			clientSecret: env.GITHUB_CLIENT_SECRET
		}
	},
	plugins: [sveltekitCookies(getRequestEvent)] // make sure this is the last plugin in the array
});

Additional context

The issue appeared when we upgraded our deps.:

  • better-auth: ^1.3.28 => ^1.4.18
  • vite: ^7.1.7 => ^7.3.1
  • svelte: ^5.39.5 => ^5.53.0
  • @sveltejs/kit": ^2.43.2 => ^2.52.2

The error also appears, when we only upgrade better-auth.

Originally created by @megla-hburdow on GitHub (Feb 24, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8125 ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce You can use the sveltekit template. The issue is present there: 1. `npx sv create example-project` 2. select better-auth in the stepper 3. remove the .env file 4. `pnpm run build` ### Current vs. Expected behavior Based on the `auth.ts` file under `src/lib/server/auth.ts`. I would expact, that the secret gets dynamicly set on runtime. (see "Auth config" section for reference...) In the Auth config it uses the `import { env } from '$env/dynamic/private';` import [which should get the env var on runitme](https://svelte.dev/docs/kit/$env-dynamic-private). Because of that I would expect, that I can set the env var on runtime and not on build time. The build seems to already require the `BETTER_AUTH_SECRET` in the build context and fails with the following error if not present: ```log 2026-02-24T12:36:51.160Z WARN [Better Auth]: [better-auth] Base URL could not be determined. Please set a valid base URL using the baseURL config option or the BETTER_AUTH_BASE_URL environment variable. Without this, callbacks and redirects may not work correctly. node:internal/event_target:1122 process.nextTick(() => { throw err; }); ^ [Error [BetterAuthError]: You are using the default secret. Please set `BETTER_AUTH_SECRET` in your environment variables or pass `secret` in your auth config.] Node.js v24.13.0  ELIFECYCLE  Command failed with exit code 1. ``` ### What version of Better Auth are you using? 1.4.18 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 11 Pro", "release": "10.0.26200", "cpuCount": 20, "cpuModel": "13th Gen Intel(R) Core(TM) i7-13800H", "totalMemory": "31.66 GB", "freeMemory": "8.38 GB" }, "node": { "version": "v24.13.0", "env": "development" }, "packageManager": { "name": "pnpm", "version": "10.30.2" }, "frameworks": [ { "name": "svelte", "version": "^5.51.0" }, { "name": "@sveltejs/kit", "version": "^2.50.2" } ], "databases": [ { "name": "@libsql/client", "version": "^0.17.0" }, { "name": "drizzle", "version": "^0.45.1" } ], "betterAuth": { "version": "Unknown", "config": null, "error": "DATABASE_URL is not set" } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from 'better-auth/minimal'; import { drizzleAdapter } from 'better-auth/adapters/drizzle'; import { sveltekitCookies } from 'better-auth/svelte-kit'; import { env } from '$env/dynamic/private'; import { getRequestEvent } from '$app/server'; import { db } from '$lib/server/db'; export const auth = betterAuth({ baseURL: env.ORIGIN, secret: env.BETTER_AUTH_SECRET, database: drizzleAdapter(db, { provider: 'sqlite' }), emailAndPassword: { enabled: true }, socialProviders: { github: { clientId: env.GITHUB_CLIENT_ID, clientSecret: env.GITHUB_CLIENT_SECRET } }, plugins: [sveltekitCookies(getRequestEvent)] // make sure this is the last plugin in the array }); ``` ### Additional context The issue appeared when we upgraded our deps.: - better-auth: ^1.3.28 => ^1.4.18 - vite: ^7.1.7 => ^7.3.1 - svelte: ^5.39.5 => ^5.53.0 - @sveltejs/kit": ^2.43.2 => ^2.52.2 The error also appears, when we only upgrade better-auth.
GiteaMirror added the platformcorebug labels 2026-04-13 07:22:50 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Feb 24, 2026):

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

This is a known issue that was introduced in Better Auth v1.4.x. The library now performs eager secret validation during async context initialization, which triggers during SvelteKit's build process before $env/dynamic/private values are available.

Recommended fix: Use the building flag in your hooks.server.ts:

import { auth } from "$lib/server/auth";
import { svelteKitHandler } from "better-auth/svelte-kit";
import { building } from "$app/environment";

export async function handle({ event, resolve }) {
  return svelteKitHandler({ event, resolve, auth, building });
}

When building is true, the handler bypasses authentication logic entirely, preventing initialization errors during the build.

This is the same issue reported in #8061.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3951641576 --> @dosubot[bot] commented on GitHub (Feb 24, 2026): <!-- Greeting --> Hi @megla-hburdow! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a known issue that was introduced in Better Auth v1.4.x. The library now performs [eager secret validation during async context initialization](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts#L50-L137), which triggers during SvelteKit's build process before `$env/dynamic/private` values are available. **Recommended fix**: Use the `building` flag in your `hooks.server.ts`: ```typescript import { auth } from "$lib/server/auth"; import { svelteKitHandler } from "better-auth/svelte-kit"; import { building } from "$app/environment"; export async function handle({ event, resolve }) { return svelteKitHandler({ event, resolve, auth, building }); } ``` [When `building` is true, the handler bypasses authentication logic entirely](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/integrations/svelte-kit.ts#L27-L29), preventing initialization errors during the build. This is [the same issue reported in #8061](https://github.com/better-auth/better-auth/issues/8061). <!-- 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/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/0f2a97c1-d0e5-46c8-ba58-6ce0c96ac71e?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20known%20issue%20that%20was%20introduced%20in%20Better%20Auth%20v1.4.x.%20The%20library%20now%20performs%20%5Beager%20secret%20validation%20during%20async%20context%20initialization%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts%23L50-L137%29%2C%20which%20triggers%20during%20SvelteKit%27s%20build%20process%20before%20%60%24env/dynamic/private%60%20values%20are%20available.%0A%0A%2A%2ARecommended%20fix%2A%2A%3A%20Use%20the%20%60building%60%20flag%20in%20your%20%60hooks.server.ts%60%3A%0A%0A%60%60%60typescript%0Aimport%20%7B%20auth%20%7D%20from%20%22%24lib/server/auth%22%3B%0Aimport%20%7B%20svelteKitHandler%20%7D%20from%20%22better-auth/svelte-kit%22%3B%0Aimport%20%7B%20building%20%7D%20from%20%22%24app/environment%22%3B%0A%0Aexport%20async%20function%20handle%28%7B%20event%2C%20resolve%20%7D%29%20%7B%0A%20%20return%20svelteKitHandler%28%7B%20event%2C%20resolve%2C%20auth%2C%20building%20%7D%29%3B%0A%7D%0A%60%60%60%0A%0A%5BWhen%20%60building%60%20is%20true%2C%20the%20handler%20bypasses%20authentication%20logic%20entirely%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/integrations/svelte-kit.ts%23L27-L29%29%2C%20preventing%20initialization%20errors%20during%20the%20build.%0A%0AThis%20is%20%5Bthe%20same%20issue%20reported%20in%20%238061%5D%28https%3A//github.com/better-auth/better-auth/issues/8061%29.)&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/8125)
Author
Owner

@megla-hburdow commented on GitHub (Feb 24, 2026):

@dosu
I already use the building flag in my project, but it still doesn't work.
The workaround from #8061 with the fallback value doesn't satisfies my requirements, because it bypasses the secret validation entirely, also in real production cases...

<!-- gh-comment-id:3952188999 --> @megla-hburdow commented on GitHub (Feb 24, 2026): @dosu I already use the building flag in my project, but it still doesn't work. The workaround from #8061 with the fallback value doesn't satisfies my requirements, because it bypasses the secret validation entirely, also in real production cases...
Author
Owner

@dosubot[bot] commented on GitHub (Feb 24, 2026):

You're right that the building flag alone isn't sufficient here. The issue is that the building flag only prevents the handler from processing requests—it doesn't prevent the betterAuth() call from executing during module evaluation, which happens before your hooks code runs.

The core problem is that Better Auth's secret validation runs eagerly during createAuthContext() when the module is imported, and SvelteKit's $env/dynamic/private isn't available at that point in the build process.

For a solution that maintains production validation, you could use a lazy initialization pattern:

// src/lib/server/auth.ts
import { building } from "$app/environment";
import { betterAuth } from "better-auth";

let authInstance: ReturnType<typeof betterAuth> | null = null;

export function getAuth() {
  if (building) return null;
  
  if (!authInstance) {
    authInstance = betterAuth({
      secret: process.env.BETTER_AUTH_SECRET,
      // ... rest of config
    });
  }
  return authInstance;
}
// src/hooks.server.ts
import { building } from "$app/environment";
import { getAuth } from "$lib/server/auth";
import { svelteKitHandler } from "better-auth/svelte-kit";

export async function handle({ event, resolve }) {
  if (building) return resolve(event);
  
  const auth = getAuth();
  if (!auth) return resolve(event);
  
  return svelteKitHandler({ event, resolve, auth });
}

This defers betterAuth() execution to runtime, so validation only happens in actual production with real environment variables.

There's also an open feature request (#3953) for native factory-function support that would address this more elegantly.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3952243425 --> @dosubot[bot] commented on GitHub (Feb 24, 2026): <!-- Answer --> You're right that the `building` flag alone isn't sufficient here. The issue is that [the `building` flag only prevents the **handler** from processing requests](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/integrations/svelte-kit.ts#L27-L28)—it doesn't prevent the `betterAuth()` call from executing during module evaluation, which happens before your hooks code runs. The core problem is that [Better Auth's secret validation runs eagerly during `createAuthContext()`](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts#L50-L85) when the module is imported, and SvelteKit's `$env/dynamic/private` isn't available at that point in the build process. For a solution that maintains production validation, you could use a lazy initialization pattern: ```typescript // src/lib/server/auth.ts import { building } from "$app/environment"; import { betterAuth } from "better-auth"; let authInstance: ReturnType<typeof betterAuth> | null = null; export function getAuth() { if (building) return null; if (!authInstance) { authInstance = betterAuth({ secret: process.env.BETTER_AUTH_SECRET, // ... rest of config }); } return authInstance; } ``` ```typescript // src/hooks.server.ts import { building } from "$app/environment"; import { getAuth } from "$lib/server/auth"; import { svelteKitHandler } from "better-auth/svelte-kit"; export async function handle({ event, resolve }) { if (building) return resolve(event); const auth = getAuth(); if (!auth) return resolve(event); return svelteKitHandler({ event, resolve, auth }); } ``` This defers `betterAuth()` execution to runtime, so validation only happens in actual production with real environment variables. There's also [an open feature request (#3953)](https://github.com/better-auth/better-auth/issues/3953) for native factory-function support that would address this more elegantly. <!-- 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/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/ddb216fa-c03f-468d-b743-48d0cf1b12bc?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=You%27re%20right%20that%20the%20%60building%60%20flag%20alone%20isn%27t%20sufficient%20here.%20The%20issue%20is%20that%20%5Bthe%20%60building%60%20flag%20only%20prevents%20the%20%2A%2Ahandler%2A%2A%20from%20processing%20requests%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/integrations/svelte-kit.ts%23L27-L28%29%E2%80%94it%20doesn%27t%20prevent%20the%20%60betterAuth%28%29%60%20call%20from%20executing%20during%20module%20evaluation%2C%20which%20happens%20before%20your%20hooks%20code%20runs.%0A%0AThe%20core%20problem%20is%20that%20%5BBetter%20Auth%27s%20secret%20validation%20runs%20eagerly%20during%20%60createAuthContext%28%29%60%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts%23L50-L85%29%20when%20the%20module%20is%20imported%2C%20and%20SvelteKit%27s%20%60%24env/dynamic/private%60%20isn%27t%20available%20at%20that%20point%20in%20the%20build%20process.%0A%0AFor%20a%20solution%20that%20maintains%20production%20validation%2C%20you%20could%20use%20a%20lazy%20initialization%20pattern%3A%0A%0A%60%60%60typescript%0A//%20src/lib/server/auth.ts%0Aimport%20%7B%20building%20%7D%20from%20%22%24app/environment%22%3B%0Aimport%20%7B%20betterAuth%20%7D%20from%20%22better-auth%22%3B%0A%0Alet%20authInstance%3A%20ReturnType%3Ctypeof%20betterAuth%3E%20%7C%20null%20%3D%20null%3B%0A%0Aexport%20function%20getAuth%28%29%20%7B%0A%20%20if%20%28building%29%20return%20null%3B%0A%20%20%0A%20%20if%20%28%21authInstance%29%20%7B%0A%20%20%20%20authInstance%20%3D%20betterAuth%28%7B%0A%20%20%20%20%20%20secret%3A%20process.env.BETTER_AUTH_SECRET%2C%0A%20%20%20%20%20%20//%20...%20rest%20of%20config%0A%20%20%20%20%7D%29%3B%0A%20%20%7D%0A%20%20return%20authInstance%3B%0A%7D%0A%60%60%60%0A%0A%60%60%60typescript%0A//%20src/hooks.server.ts%0Aimport%20%7B%20building%20%7D%20from%20%22%24app/environment%22%3B%0Aimport%20%7B%20getAuth%20%7D%20from%20%22%24lib/server/auth%22%3B%0Aimport%20%7B%20svelteKitHandler%20%7D%20from%20%22better-auth/svelte-kit%22%3B%0A%0Aexport%20async%20function%20handle%28%7B%20event%2C%20resolve%20%7D%29%20%7B%0A%20%20if%20%28building%29%20return%20resolve%28event%29%3B%0A%20%20%0A%20%20const%20auth%20%3D%20getAuth%28%29%3B%0A%20%20if%20%28%21auth%29%20return%20resolve%28event%29%3B%0A%20%20%0A%20%20return%20svelteKitHandler%28%7B%20event%2C%20resolve%2C%20auth%20%7D%29%3B%0A%7D%0A%60%60%60%0A%0AThis%20defers%20%60betterAuth%28%29%60%20execution%20to%20runtime%2C%20so%20validation%20only%20happens%20in%20actual%20production%20with%20real%20environment%20variables.%0A%0AThere%27s%20also%20%5Ban%20open%20feature%20request%20%28%233953%29%5D%28https%3A//github.com/better-auth/better-auth/issues/3953%29%20for%20native%20factory-function%20support%20that%20would%20address%20this%20more%20elegantly.)&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/8125)
Author
Owner

@megla-hburdow commented on GitHub (Feb 24, 2026):

@dosu
That results in other build time errors.
Is there a config to disable the eager secret validation behavior?

<!-- gh-comment-id:3952617118 --> @megla-hburdow commented on GitHub (Feb 24, 2026): @dosu That results in other build time errors. Is there a config to disable the eager secret validation behavior?
Author
Owner

@dosubot[bot] commented on GitHub (Feb 24, 2026):

No, there's currently no config option to disable the eager secret validation. The only way to bypass it is through test environment detection—validation is skipped when NODE_ENV=test or when the TEST environment variable is set.

As a build workaround, you could run:

NODE_ENV=test pnpm run build

But this is admittedly hacky and might affect other build behavior. This seems like a legitimate feature request for a skipValidation or deferValidation config option that would better support SvelteKit's dynamic environment variable model without requiring test mode hacks.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3952638348 --> @dosubot[bot] commented on GitHub (Feb 24, 2026): <!-- Answer --> No, there's currently no config option to disable the eager secret validation. [The only way to bypass it is through test environment detection](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts#L56-L58)—validation is skipped when [`NODE_ENV=test` or when the `TEST` environment variable is set](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/core/src/env/env-impl.ts#L59-L60). As a build workaround, you could run: ```bash NODE_ENV=test pnpm run build ``` But this is admittedly hacky and might affect other build behavior. This seems like a legitimate feature request for a `skipValidation` or `deferValidation` config option that would better support SvelteKit's dynamic environment variable model without requiring test mode hacks. <!-- 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/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/90643090-0cce-4543-8f12-8e63a1837fa4?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=No%2C%20there%27s%20currently%20no%20config%20option%20to%20disable%20the%20eager%20secret%20validation.%20%5BThe%20only%20way%20to%20bypass%20it%20is%20through%20test%20environment%20detection%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts%23L56-L58%29%E2%80%94validation%20is%20skipped%20when%20%5B%60NODE_ENV%3Dtest%60%20or%20when%20the%20%60TEST%60%20environment%20variable%20is%20set%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/core/src/env/env-impl.ts%23L59-L60%29.%0A%0AAs%20a%20build%20workaround%2C%20you%20could%20run%3A%0A%60%60%60bash%0ANODE_ENV%3Dtest%20pnpm%20run%20build%0A%60%60%60%0A%0ABut%20this%20is%20admittedly%20hacky%20and%20might%20affect%20other%20build%20behavior.%20This%20seems%20like%20a%20legitimate%20feature%20request%20for%20a%20%60skipValidation%60%20or%20%60deferValidation%60%20config%20option%20that%20would%20better%20support%20SvelteKit%27s%20dynamic%20environment%20variable%20model%20without%20requiring%20test%20mode%20hacks.)&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/8125)
Author
Owner

@simonfelding commented on GitHub (Feb 25, 2026):

I have the same issue and would love a fix. It should also work when CI=true (@dosu only seems to be talking about test env).

Maybe the eager validation should just be disabled unless NODE_ENV=production, which is where you would expect this kind of optimization to run.

<!-- gh-comment-id:3957554138 --> @simonfelding commented on GitHub (Feb 25, 2026): I have the same issue and would love a fix. It should also work when CI=true (@dosu only seems to be talking about test env). Maybe the eager validation should just be disabled unless NODE_ENV=production, which is where you would expect this kind of optimization to run.
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

@simonfelding
It kinda works as you describe it. It only validates the secret when NODE_ENV=production, but production is the default if this env var is not set.
I dislike the eager validation approach for sveltekit in general!

Currently I figured out the following workaround which preserves the secret validation in real production environment (and not at build time...):

import { building } from '$app/environment';

export const auth = betterAuth({
  // ...
  secret: building ? 'change-me' : env.BETTER_AUTH_SECRET!,
  // ...
});
<!-- gh-comment-id:3958023709 --> @megla-hburdow commented on GitHub (Feb 25, 2026): @simonfelding It kinda works as you describe it. It only validates the secret when `NODE_ENV=production`, but production is the default if this env var is not set. I dislike the eager validation approach for sveltekit in general! Currently I figured out the following workaround which preserves the secret validation in real production environment (and not at build time...): ```ts import { building } from '$app/environment'; export const auth = betterAuth({ // ... secret: building ? 'change-me' : env.BETTER_AUTH_SECRET!, // ... }); ```
Author
Owner

@simonfelding commented on GitHub (Feb 25, 2026):

@megla-hburdow I have the same fix in my code-base, but it feels dangerous to build with a hard-coded and vulnerable secret set. You have to trust that it won't be compiled into the resulting code and have the real secret ignored..

<!-- gh-comment-id:3958051082 --> @simonfelding commented on GitHub (Feb 25, 2026): @megla-hburdow I have the same fix in my code-base, but it feels dangerous to build with a hard-coded and vulnerable secret set. You have to trust that it won't be compiled into the resulting code and have the real secret ignored..
Author
Owner

@simonfelding commented on GitHub (Feb 25, 2026):

And by the way, in the code the bot referred to, it seems like it's a bug and that it is not supposed to execute during build.

Note this section

if (isTest()) {
   	return;
}

https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts#L50-L85

<!-- gh-comment-id:3958073186 --> @simonfelding commented on GitHub (Feb 25, 2026): And by the way, in the code the bot referred to, it seems like it's a bug and that it is not supposed to execute during build. Note this section ``` if (isTest()) { return; } ``` https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts#L50-L85
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

@simonfelding As far as I understand it, it only performs the validateSecret() func on build and if I try to run the app without the secret set, it fails successfully.
I agree that this isn't a fix and only a workaround, so we should still try to figure out a more permanent solution...

<!-- gh-comment-id:3958085649 --> @megla-hburdow commented on GitHub (Feb 25, 2026): @simonfelding As far as I understand it, it only performs the `validateSecret()` func on build and if I try to run the app without the secret set, it fails successfully. I agree that this isn't a fix and only a workaround, so we should still try to figure out a more permanent solution...
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

@dosu Can u figure out for us, which code change made the secret validation eager during async context initialization?

<!-- gh-comment-id:3958114417 --> @megla-hburdow commented on GitHub (Feb 25, 2026): @dosu Can u figure out for us, which code change made the secret validation eager during async context initialization?
Author
Owner

@dosubot[bot] commented on GitHub (Feb 25, 2026):

PR #6114, merged on December 1, 2025, introduced the eager secret validation. It added the validateSecret() function to create-context.ts which is called synchronously during createAuthContext() at line 137—this runs during module initialization rather than being deferred to runtime.

The PR title ("chore: update docu about auth secret") doesn't fully reflect the behavioral change—it actually added comprehensive secret validation including minimum length enforcement, default secret detection in production, and entropy estimation.

PR #6954 (merged December 23, 2025) later softened the length check from an error to a warning, but the default secret check still throws during build.

@simonfelding You're right that the isTest() check suggests there was some awareness of skipping validation in certain contexts, but no equivalent check exists for build-time scenarios. Adding a check like if (isTest() || isBuilding()) return; would be a reasonable fix for this issue.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3958160184 --> @dosubot[bot] commented on GitHub (Feb 25, 2026): <!-- Answer --> [PR #6114](https://github.com/better-auth/better-auth/pull/6114), merged on December 1, 2025, introduced the eager secret validation. It added the `validateSecret()` function to `create-context.ts` which is [called synchronously during `createAuthContext()` at line 137](https://github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts#L137)—this runs during module initialization rather than being deferred to runtime. The PR title ("chore: update docu about auth secret") doesn't fully reflect the behavioral change—it actually added comprehensive secret validation including minimum length enforcement, default secret detection in production, and entropy estimation. [PR #6954](https://github.com/better-auth/better-auth/pull/6954) (merged December 23, 2025) later softened the length check from an error to a warning, but the default secret check still throws during build. @simonfelding You're right that the `isTest()` check suggests there was some awareness of skipping validation in certain contexts, but no equivalent check exists for build-time scenarios. Adding a check like `if (isTest() || isBuilding()) return;` would be a reasonable fix for this issue. <!-- 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/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/62c94d81-4b59-4c72-bbd7-f9363758234f?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=%5BPR%20%236114%5D%28https%3A//github.com/better-auth/better-auth/pull/6114%29%2C%20merged%20on%20December%201%2C%202025%2C%20introduced%20the%20eager%20secret%20validation.%20It%20added%20the%20%60validateSecret%28%29%60%20function%20to%20%60create-context.ts%60%20which%20is%20%5Bcalled%20synchronously%20during%20%60createAuthContext%28%29%60%20at%20line%20137%5D%28https%3A//github.com/better-auth/better-auth/blob/1892ca3b0081afeaa4e3fb5c5e389a821f7fd863/packages/better-auth/src/context/create-context.ts%23L137%29%E2%80%94this%20runs%20during%20module%20initialization%20rather%20than%20being%20deferred%20to%20runtime.%0A%0AThe%20PR%20title%20%28%22chore%3A%20update%20docu%20about%20auth%20secret%22%29%20doesn%27t%20fully%20reflect%20the%20behavioral%20change%E2%80%94it%20actually%20added%20comprehensive%20secret%20validation%20including%20minimum%20length%20enforcement%2C%20default%20secret%20detection%20in%20production%2C%20and%20entropy%20estimation.%0A%0A%5BPR%20%236954%5D%28https%3A//github.com/better-auth/better-auth/pull/6954%29%20%28merged%20December%2023%2C%202025%29%20later%20softened%20the%20length%20check%20from%20an%20error%20to%20a%20warning%2C%20but%20the%20default%20secret%20check%20still%20throws%20during%20build.%0A%0A%40simonfelding%20You%27re%20right%20that%20the%20%60isTest%28%29%60%20check%20suggests%20there%20was%20some%20awareness%20of%20skipping%20validation%20in%20certain%20contexts%2C%20but%20no%20equivalent%20check%20exists%20for%20build-time%20scenarios.%20Adding%20a%20check%20like%20%60if%20%28isTest%28%29%20%7C%7C%20isBuilding%28%29%29%20return%3B%60%20would%20be%20a%20reasonable%20fix%20for%20this%20issue.)&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/8125)
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

I agree with dosu, that if (isTest() || isBuilding()) return; would be a reasonable fix, but I think it would be kinda hard to make a isBuilding() func which works with all supported frameworks (or in the best case framework agnostic)

<!-- gh-comment-id:3958215158 --> @megla-hburdow commented on GitHub (Feb 25, 2026): I agree with dosu, that `if (isTest() || isBuilding()) return;` would be a reasonable fix, but I think it would be kinda hard to make a `isBuilding()` func which works with all supported frameworks (or in the best case framework agnostic)
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

After reviewing the mentioned change and the core/env implementation, I think changing the:

if (isTest()) return;

to:

if (!isProduction) return;

The previous implementation used this guard clause and it seems more right to me.
@simonfelding What is your opinion on that proposal?

<!-- gh-comment-id:3958288548 --> @megla-hburdow commented on GitHub (Feb 25, 2026): After reviewing the mentioned change and the core/env implementation, I think changing the: ```ts if (isTest()) return; ``` to: ```ts if (!isProduction) return; ``` The previous implementation used this guard clause and it seems more right to me. @simonfelding What is your opinion on that proposal?
Author
Owner

@simonfelding commented on GitHub (Feb 25, 2026):

won't work - when sveltekit is building, it is intentionally set to production env.

I'm working on a fix :) The idea is to use a try catch block to try to load sveltekits build status, and silently continue if it fails to load (which would happen if it's not sveltekit). if sveltekit is building, set isTest to true.

<!-- gh-comment-id:3958820079 --> @simonfelding commented on GitHub (Feb 25, 2026): won't work - when sveltekit is building, it is intentionally set to production env. I'm working on a fix :) The idea is to use a try catch block to try to load sveltekits build status, and silently continue if it fails to load (which would happen if it's not sveltekit). if sveltekit is building, set isTest to true.
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

I like the approach, but could we do it in a new func?
I feel like the isTest() func isn't the right place to do this. Could we do a isBuild() func for that?

<!-- gh-comment-id:3958946602 --> @megla-hburdow commented on GitHub (Feb 25, 2026): I like the approach, but could we do it in a new func? I feel like the `isTest()` func isn't the right place to do this. Could we do a `isBuild()` func for that?
Author
Owner

@simonfelding commented on GitHub (Feb 25, 2026):

@megla-hburdow i made a PR in #8148. Can you help test?

<!-- gh-comment-id:3960012500 --> @simonfelding commented on GitHub (Feb 25, 2026): @megla-hburdow i made a PR in #8148. Can you help test?
Author
Owner

@megla-hburdow commented on GitHub (Feb 25, 2026):

@simonfelding I still would recommend to not put the isSvelteKitBuild bool into the test eval. I would recommend to do the or directly in the validateSecrete func.

<!-- gh-comment-id:3960043909 --> @megla-hburdow commented on GitHub (Feb 25, 2026): @simonfelding I still would recommend to not put the isSvelteKitBuild bool into the test eval. I would recommend to do the `or` directly in the validateSecrete func.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#11003