[GH-ISSUE #3211] Bug with SvelteKitCookies plugin - cannot find package '$app' #26844

Closed
opened 2026-04-17 17:33:42 -05:00 by GiteaMirror · 16 comments
Owner

Originally created by @saturnonearth on GitHub (Jun 28, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3211

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Followed instructions as stated in 1.3 beta docs, 1.3.0-beta.1, I get error

Cannot find package '$app' imported from /Users/xxxx/project/node_modules/.pnpm/better-auth@1.3.0-beta.1/node_modules/better-auth/dist/integrations/svelte-kit.mjs

Current vs. Expected behavior

Should work without issue.

What version of Better Auth are you using?

1.3.0-beta.1

Provide environment information

- MacOS 26
- Chrome v137.0.7151.120

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

Backend

Auth config (if applicable)


Additional context

No response

Originally created by @saturnonearth on GitHub (Jun 28, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3211 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Followed instructions as stated in 1.3 beta docs, `1.3.0-beta.1`, I get error `Cannot find package '$app' imported from /Users/xxxx/project/node_modules/.pnpm/better-auth@1.3.0-beta.1/node_modules/better-auth/dist/integrations/svelte-kit.mjs` ### Current vs. Expected behavior Should work without issue. ### What version of Better Auth are you using? 1.3.0-beta.1 ### Provide environment information ```bash - MacOS 26 - Chrome v137.0.7151.120 ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-17 17:33:42 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jun 28, 2025):

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

This is a known packaging/integration bug with Better Auth's SvelteKit integration. The error happens because the integration tries to dynamically import from $app/environment, which is a SvelteKit-only virtual module and not available to dependencies in node_modules. This causes build/runtime errors like the one you're seeing when Better Auth is used as a library dependency rather than inside the SvelteKit app source itself. There is no explicit SvelteKitCookies plugin—SvelteKit-specific logic is centralized in the integration code.

Recent changes attempted to cache the result of the dynamic import, but this doesn't resolve the fundamental issue of importing SvelteKit virtual modules from a library dependency. There are no official workarounds or guards in the current release to prevent this error, and no documentation or roadmap items address it yet. The closest related discussion is issue #3150, which covers a memory leak from repeated dynamic imports, but the underlying packaging problem remains unresolved.

For now, this means Better Auth's SvelteKit integration isn't safe to use as a dependency until the maintainers refactor the integration to isolate SvelteKit-only imports or provide a plugin mechanism that doesn't rely on $app modules in distributed code. If you need a workaround, you would have to patch or fork the integration to avoid these imports, but there is no official guidance on how to do this yet.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3015999540 --> @dosubot[bot] commented on GitHub (Jun 28, 2025): <!-- Greeting --> Hi @saturnonearth! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a known packaging/integration bug with Better Auth's SvelteKit integration. The error happens because the integration tries to dynamically import from `$app/environment`, which is a SvelteKit-only virtual module and not available to dependencies in `node_modules`. This causes build/runtime errors like the one you're seeing when Better Auth is used as a library dependency rather than inside the SvelteKit app source itself. There is no explicit SvelteKitCookies plugin—SvelteKit-specific logic is centralized in the integration code. Recent changes attempted to cache the result of the dynamic import, but this doesn't resolve the fundamental issue of importing SvelteKit virtual modules from a library dependency. There are no official workarounds or guards in the current release to prevent this error, and no documentation or roadmap items address it yet. The closest related discussion is [issue #3150](https://github.com/better-auth/better-auth/issues/3150), which covers a memory leak from repeated dynamic imports, but the underlying packaging problem remains unresolved. For now, this means Better Auth's SvelteKit integration isn't safe to use as a dependency until the maintainers refactor the integration to isolate SvelteKit-only imports or provide a plugin mechanism that doesn't rely on `$app` modules in distributed code. If you need a workaround, you would have to patch or fork the integration to avoid these imports, but there is no official guidance on how to do this yet. <!-- 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/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/5a5722cf-8dbf-427a-98c4-d9f07a39fdd0?feedback_type=other)</sup>&nbsp;&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/3211)
Author
Owner

@Kinfe123 commented on GitHub (Jun 29, 2025):

what version of svelte and svelte-kit are you using ?

<!-- gh-comment-id:3016338079 --> @Kinfe123 commented on GitHub (Jun 29, 2025): what version of svelte and svelte-kit are you using ?
Author
Owner

@rebasecase commented on GitHub (Jun 29, 2025):

beta.2 and .3 have not been pushed upstream, so I can't test this either. In the mean time you can use a combination of cookie and set-cookie-parser

import type { Cookies } from '@sveltejs/kit';
import type { SerializeOptions as _CookieSerializeOptions } from 'cookie';
import { parseString as _parseCookieString, splitCookiesString } from 'set-cookie-parser';

type CookieSameSiteParameter = 'strict' | 'lax' | 'none' | boolean;

type CookieSerializeOptions = _CookieSerializeOptions & {
  path: string;
};

type SetCookieParams = {
  name: string;
  value: string;
  options: CookieSerializeOptions;
};

function parseCookieSameSiteParameter(value?: string): CookieSameSiteParameter | undefined {
  if (value === undefined) {
    return undefined;
  }

  const valStr = value.toLowerCase();

  if (valStr === 'strict' || valStr === 'lax' || valStr === 'none') {
    return valStr;
  }

  if (valStr === 'true') {
    return true;
  }

  if (valStr === 'false') {
    return false;
  }

  return undefined;
}

export function parseCookieString(cookie: string): SetCookieParams {
  const { name, value, ...options } = _parseCookieString(cookie);

  const serializeOptions = {
    ...options,
    path: options.path || '/',
    sameSite: parseCookieSameSiteParameter(options.sameSite)
  };

  return {
    name,
    value,
    options: serializeOptions
  };
}
export function setCookiesFromResponseHeaders(cookies: Cookies, responseHeader: Headers) {
  const setCookieHeader = responseHeader.get('set-cookie');

  if (!setCookieHeader) {
    return;
  }

  splitCookiesString(setCookieHeader)
    .map(parseCookieString)
    .forEach(({ name, value, options }) => cookies.set(name, value, options));
}

usage:


default: async ({ request, cookies }) => {
  //...
  const resp = await auth.api.signInEmail({
    body: {
      email: form.data.email,
      password: form.data.password
    },
    request,
    returnHeaders: true
  });
  
  setCookiesFromResponseHeaders(cookies, resp.headers);
  // ...
}

I wrote this yesterday not realising a) 1.3.0 was in the works, b) @Kinfe123's plugin (thanks for that!)

<!-- gh-comment-id:3016571082 --> @rebasecase commented on GitHub (Jun 29, 2025): beta.2 and .3 have not been pushed upstream, so I can't test this either. In the mean time you can use a combination of `cookie` and `set-cookie-parser` ```ts import type { Cookies } from '@sveltejs/kit'; import type { SerializeOptions as _CookieSerializeOptions } from 'cookie'; import { parseString as _parseCookieString, splitCookiesString } from 'set-cookie-parser'; type CookieSameSiteParameter = 'strict' | 'lax' | 'none' | boolean; type CookieSerializeOptions = _CookieSerializeOptions & { path: string; }; type SetCookieParams = { name: string; value: string; options: CookieSerializeOptions; }; function parseCookieSameSiteParameter(value?: string): CookieSameSiteParameter | undefined { if (value === undefined) { return undefined; } const valStr = value.toLowerCase(); if (valStr === 'strict' || valStr === 'lax' || valStr === 'none') { return valStr; } if (valStr === 'true') { return true; } if (valStr === 'false') { return false; } return undefined; } export function parseCookieString(cookie: string): SetCookieParams { const { name, value, ...options } = _parseCookieString(cookie); const serializeOptions = { ...options, path: options.path || '/', sameSite: parseCookieSameSiteParameter(options.sameSite) }; return { name, value, options: serializeOptions }; } export function setCookiesFromResponseHeaders(cookies: Cookies, responseHeader: Headers) { const setCookieHeader = responseHeader.get('set-cookie'); if (!setCookieHeader) { return; } splitCookiesString(setCookieHeader) .map(parseCookieString) .forEach(({ name, value, options }) => cookies.set(name, value, options)); } ``` usage: ```ts default: async ({ request, cookies }) => { //... const resp = await auth.api.signInEmail({ body: { email: form.data.email, password: form.data.password }, request, returnHeaders: true }); setCookiesFromResponseHeaders(cookies, resp.headers); // ... } ``` I wrote this yesterday not realising a) 1.3.0 was in the works, b) @Kinfe123's plugin (thanks for that!)
Author
Owner

@rebasecase commented on GitHub (Jun 29, 2025):

1.3.0-beta.1

"svelte": "^5.34.9",
"@sveltejs/kit": "^2.22.2",
"@sveltejs/vite-plugin-svelte": "^5.1.0",

pnpm 10.12.3

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '$app' imported from /workspaces/project/node_modules/.pnpm/better-auth@1.3.0-beta.1/node_modules/better-auth/dist/integrations/svelte-kit.mjs
    at Object.getPackageJSONURL (node:internal/modules/package_json_reader:256:9)
    at packageResolve (node:internal/modules/esm/resolve:768:81)
    at moduleResolve (node:internal/modules/esm/resolve:854:18)
    at defaultResolve (node:internal/modules/esm/resolve:984:11)
    at nextResolve (node:internal/modules/esm/hooks:748:28)
    at o (file:///workspaces/project/node_modules/.pnpm/@tailwindcss+node@4.1.11/node_modules/@tailwindcss/node/dist/esm-cache.loader.mjs:1:69)
    at nextResolve (node:internal/modules/esm/hooks:748:28)
    at Hooks.resolve (node:internal/modules/esm/hooks:240:30)
    at MessagePort.handleMessage (node:internal/modules/esm/worker:199:24)
    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:827:20) {
  code: 'ERR_MODULE_NOT_FOUND'
<!-- gh-comment-id:3016579939 --> @rebasecase commented on GitHub (Jun 29, 2025): 1.3.0-beta.1 ``` "svelte": "^5.34.9", "@sveltejs/kit": "^2.22.2", "@sveltejs/vite-plugin-svelte": "^5.1.0", ``` pnpm 10.12.3 ``` Error [ERR_MODULE_NOT_FOUND]: Cannot find package '$app' imported from /workspaces/project/node_modules/.pnpm/better-auth@1.3.0-beta.1/node_modules/better-auth/dist/integrations/svelte-kit.mjs at Object.getPackageJSONURL (node:internal/modules/package_json_reader:256:9) at packageResolve (node:internal/modules/esm/resolve:768:81) at moduleResolve (node:internal/modules/esm/resolve:854:18) at defaultResolve (node:internal/modules/esm/resolve:984:11) at nextResolve (node:internal/modules/esm/hooks:748:28) at o (file:///workspaces/project/node_modules/.pnpm/@tailwindcss+node@4.1.11/node_modules/@tailwindcss/node/dist/esm-cache.loader.mjs:1:69) at nextResolve (node:internal/modules/esm/hooks:748:28) at Hooks.resolve (node:internal/modules/esm/hooks:240:30) at MessagePort.handleMessage (node:internal/modules/esm/worker:199:24) at [nodejs.internal.kHybridDispatch] (node:internal/event_target:827:20) { code: 'ERR_MODULE_NOT_FOUND' ```
Author
Owner

@rebasecase commented on GitHub (Jun 29, 2025):

Adding it to vite's ssr.noExternal works, but I do not yet understand the implications of this...

i.e.

ssr: {
  noExternal: ['better-auth'] // Add the problematic package here
}
<!-- gh-comment-id:3016587746 --> @rebasecase commented on GitHub (Jun 29, 2025): Adding it to vite's `ssr.noExternal` works, but I do not yet understand the implications of this... i.e. ``` ssr: { noExternal: ['better-auth'] // Add the problematic package here } ```
Author
Owner

@rebasecase commented on GitHub (Jul 8, 2025):

Still broken with 1.3.0-beta.6

<!-- gh-comment-id:3048493061 --> @rebasecase commented on GitHub (Jul 8, 2025): Still broken with 1.3.0-beta.6
Author
Owner

@jowsey commented on GitHub (Jul 20, 2025):

Made it to stable yet this still appears to be the case? Also breaks @better-auth/cli in particular (ERROR [Better Auth]: [#better-auth]: Couldn't read your auth config. error: Cannot find module '$app/server' ...) when following the example in the docs.

<!-- gh-comment-id:3094764233 --> @jowsey commented on GitHub (Jul 20, 2025): Made it to stable yet this still appears to be the case? Also breaks @better-auth/cli in particular (`ERROR [Better Auth]: [#better-auth]: Couldn't read your auth config. error: Cannot find module '$app/server' ...`) when following the example in the docs.
Author
Owner

@saturnonearth commented on GitHub (Jul 20, 2025):

Made it to stable yet this still appears to be the case? Also breaks @better-auth/cli in particular (ERROR [Better Auth]: [#better-auth]: Couldn't read your auth config. error: Cannot find module '$app/server' ...) when following the example in the docs.

You have to pass build now and getRequestEvent now. The docs are accurate at this point - except I did sveltekitCookies(async () => getRequestEvent()) instead of sveltekitCookies(getRequestEvent)

<!-- gh-comment-id:3094810312 --> @saturnonearth commented on GitHub (Jul 20, 2025): > Made it to stable yet this still appears to be the case? Also breaks @better-auth/cli in particular (`ERROR [Better Auth]: [#better-auth]: Couldn't read your auth config. error: Cannot find module '$app/server' ...`) when following the example in the docs. You have to pass `build` now and `getRequestEvent` now. The docs are accurate at this point - except I did `sveltekitCookies(async () => getRequestEvent())` instead of `sveltekitCookies(getRequestEvent)`
Author
Owner

@jowsey commented on GitHub (Jul 20, 2025):

You have to pass build now and getRequestEvent now. The docs are accurate at this point - except I did sveltekitCookies(async () => getRequestEvent()) instead of sveltekitCookies(getRequestEvent)

Good shout on the async function, that does mitigate a typing issue I also ran into, but other than that I'm passing everything just as the docs say. Presumably the problem lies in trying to import $app/server?

<!-- gh-comment-id:3094829895 --> @jowsey commented on GitHub (Jul 20, 2025): > You have to pass `build` now and `getRequestEvent` now. The docs are accurate at this point - except I did `sveltekitCookies(async () => getRequestEvent())` instead of `sveltekitCookies(getRequestEvent)` Good shout on the async function, that does mitigate a typing issue I also ran into, but other than that I'm passing everything just as the docs say. Presumably the problem lies in trying to import `$app/server`?
Author
Owner

@Kinfe123 commented on GitHub (Jul 21, 2025):

Where are you importing $app/server ?

<!-- gh-comment-id:3097366573 --> @Kinfe123 commented on GitHub (Jul 21, 2025): Where are you importing $app/server ?
Author
Owner

@rebasecase commented on GitHub (Jul 21, 2025):

The docs are accurate at this point

Docs say to explicitly sveltekitCookies(getRequestEvent) which is wrong isn't it?

Argument of type '() => RequestEvent<Partial<Record<string, string>>, string | null>' is not assignable to parameter of type '() => Promise<RequestEvent<Partial<Record<string, string>>, string | null>>'.

What I wonder is how does it work in environments without AsyncLocalStorage? I haven't enough experience to understand this yet.
In environments without `AsyncLocalStorage`, [getRequestEvent] must be read synchronously, not after an `await`.';

Otherwise it all works perfectly, thank you so much.

<!-- gh-comment-id:3097416231 --> @rebasecase commented on GitHub (Jul 21, 2025): > The docs are accurate at this point Docs say to explicitly `sveltekitCookies(getRequestEvent)` which is wrong isn't it? ``` Argument of type '() => RequestEvent<Partial<Record<string, string>>, string | null>' is not assignable to parameter of type '() => Promise<RequestEvent<Partial<Record<string, string>>, string | null>>'. ``` What I wonder is how does it work in environments without AsyncLocalStorage? I haven't enough experience to understand this yet. ````In environments without `AsyncLocalStorage`, [getRequestEvent] must be read synchronously, not after an `await`.';```` Otherwise it all works perfectly, thank you so much.
Author
Owner

@rebasecase commented on GitHub (Jul 21, 2025):

@jowsey I have the same issue with the CLI also

npx @better-auth/cli@latest generate  --config ./src/lib/server/auth/index.ts
2025-07-21T16:29:26.308Z ERROR [Better Auth]: Couldn't read your auth config. Error: Cannot find module '$lib/server/db'

Which is to be expected (?) since $lib is a typescript alias

Running it through tsx causes the same failure you are experiencing, I imagine since $app is a sveltekit alias (not sure of the exact mechanism).

pnpm tsx node_modules/@better-auth/cli/dist/index.mjs generate --config ./src/lib/server/auth/index.ts
2025-07-21T16:30:37.754Z ERROR [Better Auth]: Couldn't read your auth config. Error: Cannot find module '$app/server'

Perhaps this warrants a separate ticket?

For the time being you can just remove $app reference and the sveltekit cookies plugin before you run the cli.

@Kinfe123 I am importing $app/server at the top of my config file: ./src/lib/server/auth/index.ts

<!-- gh-comment-id:3097507523 --> @rebasecase commented on GitHub (Jul 21, 2025): @jowsey I have the same issue with the CLI also ``` npx @better-auth/cli@latest generate --config ./src/lib/server/auth/index.ts 2025-07-21T16:29:26.308Z ERROR [Better Auth]: Couldn't read your auth config. Error: Cannot find module '$lib/server/db' ``` Which is to be expected (?) since $lib is a typescript alias Running it through `tsx` causes the same failure you are experiencing, I imagine since $app is a sveltekit alias (not sure of the exact mechanism). ``` pnpm tsx node_modules/@better-auth/cli/dist/index.mjs generate --config ./src/lib/server/auth/index.ts 2025-07-21T16:30:37.754Z ERROR [Better Auth]: Couldn't read your auth config. Error: Cannot find module '$app/server' ``` Perhaps this warrants a separate ticket? For the time being you can just remove $app reference and the sveltekit cookies plugin before you run the cli. @Kinfe123 I am importing `$app/server` at the top of my config file: `./src/lib/server/auth/index.ts`
Author
Owner

@DarthGigi commented on GitHub (Aug 11, 2025):

Why has this issue been closed? The PR #3533 resolves the type error of getRequestEvent, and the error didn't affect the behavior in any way.

The issue that the Better-Auth CLI can't find the $app/server module is still present, which is what this issue is about.

<!-- gh-comment-id:3176042285 --> @DarthGigi commented on GitHub (Aug 11, 2025): Why has this issue been closed? The PR #3533 resolves the type error of `getRequestEvent`, and the error didn't affect the behavior in any way. The issue that the Better-Auth CLI can't find the `$app/server` module is still present, which is what this issue is about.
Author
Owner

@rebasecase commented on GitHub (Aug 11, 2025):

@DarthGigi dunno, additionally #3533 doesn't fix the type error either, I just forced it to unknown. For the schema generation commented out the $app virtual aliases etc when running the command. Less than ideal but I'm not sure how to fix it, I am trying to understand it!

<!-- gh-comment-id:3176215745 --> @rebasecase commented on GitHub (Aug 11, 2025): @DarthGigi dunno, additionally #3533 doesn't fix the type error either, I just forced it to unknown. For the schema generation commented out the $app virtual aliases etc when running the command. Less than ideal but I'm not sure how to fix it, I am trying to understand it!
Author
Owner

@DarthGigi commented on GitHub (Aug 11, 2025):

@rebasecase The PR did fix the type error for me 🤷🏼

<!-- gh-comment-id:3176228981 --> @DarthGigi commented on GitHub (Aug 11, 2025): @rebasecase The PR did fix the type error for me 🤷🏼
Author
Owner

@rebasecase commented on GitHub (Aug 11, 2025):

I still get

Argument of type '() => RequestEvent<LayoutParams<"/">, any>' is not assignable to parameter of type '() => RequestEvent<Partial<Record<string, string>>, string | null>'.
  Type 'RequestEvent<LayoutParams<"/">, any>' is not assignable to type 'RequestEvent<Partial<Record<string, string>>, string | null>'.
    Type 'LayoutParams<"/">' is not assignable to type 'Partial<Record<string, string>>'.
      Type 'undefined' is not assignable to type 'Partial<Record<string, string>>'.ts(2345)

Considering they've just silently added telementry also, I might just drop this library - it's good, but I can't trust that underhandedness.

This is "better-auth": "^1.3.5", "@better-auth/cli": "^1.3.5",

<!-- gh-comment-id:3176257191 --> @rebasecase commented on GitHub (Aug 11, 2025): I still get ``` Argument of type '() => RequestEvent<LayoutParams<"/">, any>' is not assignable to parameter of type '() => RequestEvent<Partial<Record<string, string>>, string | null>'. Type 'RequestEvent<LayoutParams<"/">, any>' is not assignable to type 'RequestEvent<Partial<Record<string, string>>, string | null>'. Type 'LayoutParams<"/">' is not assignable to type 'Partial<Record<string, string>>'. Type 'undefined' is not assignable to type 'Partial<Record<string, string>>'.ts(2345) ``` Considering they've just silently added telementry also, I might just drop this library - it's good, but I can't trust that underhandedness. This is "better-auth": "^1.3.5", "@better-auth/cli": "^1.3.5",
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#26844