[GH-ISSUE #7637] Using access control with the organisation client causes a huge bundle size. #28184

Closed
opened 2026-04-17 19:36:09 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @irishgreencitrus on GitHub (Jan 27, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7637

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create a better-auth instance as shown in the docs using any framework with a bundler.
  2. Add the organisation plugin to the server.
  3. Add the organisationClient plugin to the client.
    Create custom access control e.g.:
import { createAccessControl } from 'better-auth/plugins';
import {
	adminAc,
	defaultStatements,
	memberAc,
	ownerAc
} from 'better-auth/plugins/organization/access';

export const statement = {
	...defaultStatements,
	site: ['create', 'update', 'delete'],
} as const;

export const ac = createAccessControl(statement);

export const owner = ac.newRole({
	...ownerAc.statements,
	site: ['create', 'update', 'delete'],
});

export const admin = ac.newRole({
	...adminAc.statements,
	site: ['create', 'update', 'delete']
});

export const editor = ac.newRole({
       ...memberAc.statements,
       site: ['update']
});

export const member = ac.newRole({
	...memberAc.statements
});
  1. Doing as the docs say, import this file both into the server and the client.
  2. Watch your client bundle size explode!

Current vs. Expected behavior

The current behaviour is that using access control on the client causes server dependencies to be pulled into the client. This should either be resolved or an alternative package for defining access control on the client should be provided.

What version of Better Auth are you using?

1.4.17

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:08:48 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8132",
    "release": "25.2.0",
    "cpuCount": 10,
    "cpuModel": "Apple M4",
    "totalMemory": "16.00 GB",
    "freeMemory": "0.15 GB"
  },
  "node": {
    "version": "v25.4.0",
    "env": "development"
  },
  "packageManager": {
    "name": "pnpm",
    "version": "10.28.1"
  },
  "frameworks": [
    {
      "name": "svelte",
      "version": "^5.46.4"
    },
    {
      "name": "@sveltejs/kit",
      "version": "^2.49.5"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.17.2"
    },
    {
      "name": "drizzle",
      "version": "^1.0.0-beta.12-a5629fb"
    }
  ],
  "betterAuth": {
    "version": "^1.4.17",
    "config": null
  }
}

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

Client, Backend

Auth config (if applicable)

import { betterAuth } from 'better-auth/minimal';
import { drizzleAdapter } from '@better-auth/drizzle-adapter';
import { db } from '../db/db';
import { sveltekitCookies } from 'better-auth/svelte-kit';
import { getRequestEvent } from '$app/server';
import * as authSchema from '../db/schema/auth-schema';
import { admin, organization } from 'better-auth/plugins';
import {
	ac,
	admin as adminRole,
	approver,
	inviter,
	member,
	office,
	owner,
	siteManager
} from '$lib/permissions';

export const auth = betterAuth({
	emailAndPassword: {
		enabled: true
	},
	plugins: [
		admin(),
		organization({
			teams: { enabled: false },
			ac,
			roles: {
				owner,
				admin: adminRole,
				approver,
				office,
				siteManager,
				inviter,
				member
			}
		}),
		sveltekitCookies(getRequestEvent)
	],
	database: drizzleAdapter(db, {
		provider: 'pg',
		schema: authSchema
	})
});

Additional context

Removing the access control from the client causes the bundle to shrink down as expected, but without the niceties of typescript typing.

Originally created by @irishgreencitrus on GitHub (Jan 27, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7637 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Create a better-auth instance as shown in the docs using any framework with a bundler. 2. Add the organisation plugin to the server. 3. Add the organisationClient plugin to the client. Create custom access control e.g.: ```ts import { createAccessControl } from 'better-auth/plugins'; import { adminAc, defaultStatements, memberAc, ownerAc } from 'better-auth/plugins/organization/access'; export const statement = { ...defaultStatements, site: ['create', 'update', 'delete'], } as const; export const ac = createAccessControl(statement); export const owner = ac.newRole({ ...ownerAc.statements, site: ['create', 'update', 'delete'], }); export const admin = ac.newRole({ ...adminAc.statements, site: ['create', 'update', 'delete'] }); export const editor = ac.newRole({ ...memberAc.statements, site: ['update'] }); export const member = ac.newRole({ ...memberAc.statements }); ``` 4. Doing as the docs say, import this file both into the server and the client. 5. Watch your client bundle size explode! ### Current vs. Expected behavior The current behaviour is that using access control on the client causes server dependencies to be pulled into the client. This should either be resolved or an alternative package for defining access control on the client should be provided. ### What version of Better Auth are you using? 1.4.17 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:08:48 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8132", "release": "25.2.0", "cpuCount": 10, "cpuModel": "Apple M4", "totalMemory": "16.00 GB", "freeMemory": "0.15 GB" }, "node": { "version": "v25.4.0", "env": "development" }, "packageManager": { "name": "pnpm", "version": "10.28.1" }, "frameworks": [ { "name": "svelte", "version": "^5.46.4" }, { "name": "@sveltejs/kit", "version": "^2.49.5" } ], "databases": [ { "name": "pg", "version": "^8.17.2" }, { "name": "drizzle", "version": "^1.0.0-beta.12-a5629fb" } ], "betterAuth": { "version": "^1.4.17", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Client, Backend ### Auth config (if applicable) ```typescript import { betterAuth } from 'better-auth/minimal'; import { drizzleAdapter } from '@better-auth/drizzle-adapter'; import { db } from '../db/db'; import { sveltekitCookies } from 'better-auth/svelte-kit'; import { getRequestEvent } from '$app/server'; import * as authSchema from '../db/schema/auth-schema'; import { admin, organization } from 'better-auth/plugins'; import { ac, admin as adminRole, approver, inviter, member, office, owner, siteManager } from '$lib/permissions'; export const auth = betterAuth({ emailAndPassword: { enabled: true }, plugins: [ admin(), organization({ teams: { enabled: false }, ac, roles: { owner, admin: adminRole, approver, office, siteManager, inviter, member } }), sveltekitCookies(getRequestEvent) ], database: drizzleAdapter(db, { provider: 'pg', schema: authSchema }) }); ``` ### Additional context Removing the access control from the client causes the bundle to shrink down as expected, but without the niceties of typescript typing.
GiteaMirror added the lockedenhancement labels 2026-04-17 19:36:09 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jan 27, 2026):

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

The bundle size explosion happens because importing access control helpers (like createAccessControl) and organization access statements from better-auth/plugins or better-auth/plugins/organization/access pulls in server-side dependencies—like kysely, database adapters, and even zod—into your client bundle. This is due to how some plugins and helpers are structured: client and server logic are bundled together, and some imports (especially from better-auth/plugins) re-export server code, which bundlers like Vite or Webpack can't always tree-shake away, even if you only use types or type-only imports. This is a known issue in Better Auth's package structure see discussion.

To avoid this, only import client plugins from better-auth/client/plugins in your client code, and never import from better-auth, better-auth/plugins, or any server adapter/config in client files see workaround. For access control, import createAccessControl from better-auth/plugins/access, not from better-auth/plugins. Also, split your permissions and plugin code into separate files for server and client, and avoid re-exporting client plugins from server plugin index files. For shared types, put them in files that don't import any server code, and use import type for types to help bundlers skip runtime code.

If you're using Vite, add optimizeDeps: { exclude: ['better-auth'] } to your config to help prevent server code from leaking into the client bundle see Vite workaround.

The underlying cause is that the access control helper imports BetterAuthError from @better-auth/core/error, which pulls in a chain of utilities and dependencies (like zod) that you don't need on the client. Fixing this fully would require Better Auth to refactor its client/server separation or provide a lightweight client-only access control helper see technical note.

For now, the best practice is to strictly separate your client and server imports, use the correct entrypoints, and avoid re-exports that mix client and server code. This will keep your client bundle lean while retaining TypeScript type safety.

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:3806834467 --> @dosubot[bot] commented on GitHub (Jan 27, 2026): <!-- Greeting --> Hi @irishgreencitrus! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The bundle size explosion happens because importing access control helpers (like createAccessControl) and organization access statements from better-auth/plugins or better-auth/plugins/organization/access pulls in server-side dependencies—like kysely, database adapters, and even zod—into your client bundle. This is due to how some plugins and helpers are structured: client and server logic are bundled together, and some imports (especially from better-auth/plugins) re-export server code, which bundlers like Vite or Webpack can't always tree-shake away, even if you only use types or type-only imports. This is a known issue in Better Auth's package structure [see discussion](https://github.com/better-auth/better-auth/issues/7085). To avoid this, only import client plugins from better-auth/client/plugins in your client code, and never import from better-auth, better-auth/plugins, or any server adapter/config in client files [see workaround](https://github.com/better-auth/better-auth/issues/5539). For access control, import createAccessControl from better-auth/plugins/access, not from better-auth/plugins. Also, split your permissions and plugin code into separate files for server and client, and avoid re-exporting client plugins from server plugin index files. For shared types, put them in files that don't import any server code, and use import type for types to help bundlers skip runtime code. If you're using Vite, add optimizeDeps: { exclude: ['better-auth'] } to your config to help prevent server code from leaking into the client bundle [see Vite workaround](https://github.com/better-auth/better-auth/issues/4457). The underlying cause is that the access control helper imports BetterAuthError from @better-auth/core/error, which pulls in a chain of utilities and dependencies (like zod) that you don't need on the client. Fixing this fully would require Better Auth to refactor its client/server separation or provide a lightweight client-only access control helper [see technical note](https://github.com/better-auth/better-auth/blob/d1602ef0ae9822e462a5a42497072a0a1756e804/packages/better-auth/src/plugins/access/access.ts#L1-L88). For now, the best practice is to strictly separate your client and server imports, use the correct entrypoints, and avoid re-exports that mix client and server code. This will keep your client bundle lean while retaining TypeScript type safety. <!-- 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/a84c56c6-501e-4c73-914d-e8ace24daf61?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/a84c56c6-501e-4c73-914d-e8ace24daf61?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/a84c56c6-501e-4c73-914d-e8ace24daf61?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/a84c56c6-501e-4c73-914d-e8ace24daf61?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/a84c56c6-501e-4c73-914d-e8ace24daf61?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/a84c56c6-501e-4c73-914d-e8ace24daf61?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/a84c56c6-501e-4c73-914d-e8ace24daf61?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=The%20bundle%20size%20explosion%20happens%20because%20importing%20access%20control%20helpers%20%28like%20createAccessControl%29%20and%20organization%20access%20statements%20from%20better-auth/plugins%20or%20better-auth/plugins/organization/access%20pulls%20in%20server-side%20dependencies%E2%80%94like%20kysely%2C%20database%20adapters%2C%20and%20even%20zod%E2%80%94into%20your%20client%20bundle.%20This%20is%20due%20to%20how%20some%20plugins%20and%20helpers%20are%20structured%3A%20client%20and%20server%20logic%20are%20bundled%20together%2C%20and%20some%20imports%20%28especially%20from%20better-auth/plugins%29%20re-export%20server%20code%2C%20which%20bundlers%20like%20Vite%20or%20Webpack%20can%27t%20always%20tree-shake%20away%2C%20even%20if%20you%20only%20use%20types%20or%20type-only%20imports.%20This%20is%20a%20known%20issue%20in%20Better%20Auth%27s%20package%20structure%C2%A0%5Bsee%20discussion%5D%28https%3A//github.com/better-auth/better-auth/issues/7085%29.%0A%0ATo%20avoid%20this%2C%20only%20import%20client%20plugins%20from%20better-auth/client/plugins%20in%20your%20client%20code%2C%20and%20never%20import%20from%20better-auth%2C%20better-auth/plugins%2C%20or%20any%20server%20adapter/config%20in%20client%20files%C2%A0%5Bsee%20workaround%5D%28https%3A//github.com/better-auth/better-auth/issues/5539%29.%20For%20access%20control%2C%20import%20createAccessControl%20from%20better-auth/plugins/access%2C%20not%20from%20better-auth/plugins.%20Also%2C%20split%20your%20permissions%20and%20plugin%20code%20into%20separate%20files%20for%20server%20and%20client%2C%20and%20avoid%20re-exporting%20client%20plugins%20from%20server%20plugin%20index%20files.%20For%20shared%20types%2C%20put%20them%20in%20files%20that%20don%27t%20import%20any%20server%20code%2C%20and%20use%20import%20type%20for%20types%20to%20help%20bundlers%20skip%20runtime%20code.%0A%0AIf%20you%27re%20using%20Vite%2C%20add%20optimizeDeps%3A%20%7B%20exclude%3A%20%5B%27better-auth%27%5D%20%7D%20to%20your%20config%20to%20help%20prevent%20server%20code%20from%20leaking%20into%20the%20client%20bundle%C2%A0%5Bsee%20Vite%20workaround%5D%28https%3A//github.com/better-auth/better-auth/issues/4457%29.%0A%0AThe%20underlying%20cause%20is%20that%20the%20access%20control%20helper%20imports%20BetterAuthError%20from%20%40better-auth/core/error%2C%20which%20pulls%20in%20a%20chain%20of%20utilities%20and%20dependencies%20%28like%20zod%29%20that%20you%20don%27t%20need%20on%20the%20client.%20Fixing%20this%20fully%20would%20require%20Better%20Auth%20to%20refactor%20its%20client/server%20separation%20or%20provide%20a%20lightweight%20client-only%20access%20control%20helper%C2%A0%5Bsee%20technical%20note%5D%28https%3A//github.com/better-auth/better-auth/blob/d1602ef0ae9822e462a5a42497072a0a1756e804/packages/better-auth/src/plugins/access/access.ts%23L1-L88%29.%0A%0AFor%20now%2C%20the%20best%20practice%20is%20to%20strictly%20separate%20your%20client%20and%20server%20imports%2C%20use%20the%20correct%20entrypoints%2C%20and%20avoid%20re-exports%20that%20mix%20client%20and%20server%20code.%20This%20will%20keep%20your%20client%20bundle%20lean%20while%20retaining%20TypeScript%20type%20safety.)&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/7637)
Author
Owner

@irishgreencitrus commented on GitHub (Jan 27, 2026):

@dosu The documentation should be updated to reflect the fact that importing from 'better-auth/plugins' instead of the correct 'better-auth/plugins/access' in the 'permission.ts' file will make your bundle size be huge as access control is one of the areas in the docs where it recommends sharing a file between client and server.

<!-- gh-comment-id:3806909483 --> @irishgreencitrus commented on GitHub (Jan 27, 2026): @dosu The documentation should be updated to reflect the fact that importing from 'better-auth/plugins' instead of the correct 'better-auth/plugins/access' in the 'permission.ts' file will make your bundle size be huge as access control is one of the areas in the docs where it recommends sharing a file between client and server.
Author
Owner

@dosubot[bot] commented on GitHub (Jan 27, 2026):

You're absolutely right—there's currently no warning in the docs about the bundle size impact of importing from 'better-auth/plugins' instead of 'better-auth/plugins/access' in shared permission files. While the official documentation does show the correct import path for createAccessControl and recommends sharing the permissions file between client and server, it doesn't mention that using the wrong path can pull in server dependencies and dramatically increase your client bundle size (source, docs).

The technical reason is that 'better-auth/plugins' re-exports server-side modules, so if you use that path in a shared file, your bundler will include things like Kysely, Zod, and other backend dependencies in your client build (source). IntelliSense can sometimes suggest the shorter path, which makes this easy to miss.

Best practice: always use 'better-auth/plugins/access' for access control in shared files, and audit your imports to avoid accidental server code in the client. Until the docs are updated, it's worth adding a comment in your own codebase to warn teammates about this pitfall.

If you want to avoid this issue right now, double-check your import paths and use explicit client-only entrypoints for anything that runs in the browser.

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:3806939462 --> @dosubot[bot] commented on GitHub (Jan 27, 2026): <!-- Answer --> You're absolutely right—there's currently no warning in the docs about the bundle size impact of importing from `'better-auth/plugins'` instead of `'better-auth/plugins/access'` in shared permission files. While the official documentation does show the correct import path for `createAccessControl` and recommends sharing the permissions file between client and server, it doesn't mention that using the wrong path can pull in server dependencies and dramatically increase your client bundle size ([source](https://github.com/better-auth/better-auth/issues/5539), [docs](https://github.com/better-auth/better-auth/blob/d1602ef0ae9822e462a5a42497072a0a1756e804/docs/content/docs/plugins/organization.mdx#L1257-L1370)). The technical reason is that `'better-auth/plugins'` re-exports server-side modules, so if you use that path in a shared file, your bundler will include things like Kysely, Zod, and other backend dependencies in your client build ([source](https://github.com/better-auth/better-auth/issues/7085)). IntelliSense can sometimes suggest the shorter path, which makes this easy to miss. Best practice: always use `'better-auth/plugins/access'` for access control in shared files, and audit your imports to avoid accidental server code in the client. Until the docs are updated, it's worth adding a comment in your own codebase to warn teammates about this pitfall. If you want to avoid this issue right now, double-check your import paths and use explicit client-only entrypoints for anything that runs in the browser. <!-- 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/ace1766c-d480-4c7c-8f9c-44bd59c8f966?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/ace1766c-d480-4c7c-8f9c-44bd59c8f966?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/ace1766c-d480-4c7c-8f9c-44bd59c8f966?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/ace1766c-d480-4c7c-8f9c-44bd59c8f966?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/ace1766c-d480-4c7c-8f9c-44bd59c8f966?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/ace1766c-d480-4c7c-8f9c-44bd59c8f966?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/ace1766c-d480-4c7c-8f9c-44bd59c8f966?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%20absolutely%20right%E2%80%94there%27s%20currently%20no%20warning%20in%20the%20docs%20about%20the%20bundle%20size%20impact%20of%20importing%20from%20%60%27better-auth/plugins%27%60%20instead%20of%20%60%27better-auth/plugins/access%27%60%20in%20shared%20permission%20files.%20While%20the%20official%20documentation%20does%20show%20the%20correct%20import%20path%20for%20%60createAccessControl%60%20and%20recommends%20sharing%20the%20permissions%20file%20between%20client%20and%20server%2C%20it%20doesn%27t%20mention%20that%20using%20the%20wrong%20path%20can%20pull%20in%20server%20dependencies%20and%20dramatically%20increase%20your%20client%20bundle%20size%C2%A0%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/5539%29%2C%20%5Bdocs%5D%28https%3A//github.com/better-auth/better-auth/blob/d1602ef0ae9822e462a5a42497072a0a1756e804/docs/content/docs/plugins/organization.mdx%23L1257-L1370%29%29.%0A%0AThe%20technical%20reason%20is%20that%20%60%27better-auth/plugins%27%60%20re-exports%20server-side%20modules%2C%20so%20if%20you%20use%20that%20path%20in%20a%20shared%20file%2C%20your%20bundler%20will%20include%20things%20like%20Kysely%2C%20Zod%2C%20and%20other%20backend%20dependencies%20in%20your%20client%20build%C2%A0%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/7085%29%29.%20IntelliSense%20can%20sometimes%20suggest%20the%20shorter%20path%2C%20which%20makes%20this%20easy%20to%20miss.%0A%0ABest%20practice%3A%20always%20use%20%60%27better-auth/plugins/access%27%60%20for%20access%20control%20in%20shared%20files%2C%20and%20audit%20your%20imports%20to%20avoid%20accidental%20server%20code%20in%20the%20client.%20Until%20the%20docs%20are%20updated%2C%20it%27s%20worth%20adding%20a%20comment%20in%20your%20own%20codebase%20to%20warn%20teammates%20about%20this%20pitfall.%0A%0AIf%20you%20want%20to%20avoid%20this%20issue%20right%20now%2C%20double-check%20your%20import%20paths%20and%20use%20explicit%20client-only%20entrypoints%20for%20anything%20that%20runs%20in%20the%20browser.)&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/7637)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28184