Organization Plugin on version 1.3.26 breaks: Dynamic access control set to true breaks the types #2081

Closed
opened 2026-03-13 09:25:25 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @rohovskoi on GitHub (Oct 6, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

To reproduce you will need:

organization({
            requireEmailVerificationOnInvitation: true,
            ac: orgAc,
            creatorRole: 'owner',
            roles: {
              owner,
              admin,
              customer,
            },
            teams: {
              enabled: true
            },
            dynamicAccessControl: {
              maximumRolesPerOrganization: 20,
              enabled: true
            }
          }),

Current vs. Expected behavior

Current: plugin breaks.
Expected: types of the organization plugin are inferred and betterAuth() client works as expected.

What version of Better Auth are you using?

1.3.26

System info

.

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

Package

Auth config (if applicable)


Additional context

Error:

Type '{ id: "organization"; endpoints: { createOrganization: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0: { body: { name: string; slug: string; userId?: string | undefined; logo?: string | undefined; metadata?: Record<...> | undefined; keepCurrentActiveOrganization?: boolean | ...' is not assignable to type 'BetterAuthPlugin'.
Types of property 'endpoints' are incompatible.
Type '{ createOrganization: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0: { body: { name: string; slug: string; userId?: string | undefined; logo?: string | undefined; metadata?: Record<string, any> | undefined; keepCurrentActiveOrganization?: boolean | undefined; }; } & ... 6 m...' is not assignable to type '{ [key: string]: Endpoint; }'.
Property 'listOrgRoles' is incompatible with index signature.
Type '{ <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: { organizationId?: string | undefined; } | undefined; } & { params?: Record<string, any>; } & { ...; } & { ...; } & { ...; } & { ...; }) | undefined): Promis...' is not assignable to type 'Endpoint'.
Type '{ <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: { organizationId?: string | undefined; } | undefined; } & { params?: Record<string, any>; } & { ...; } & { ...; } & { ...; } & { ...; }) | undefined): Promis...' is not assignable to type '{ options: EndpointOptions; path: string; }'.
The types of 'options.metadata.$Infer.query' are incompatible between these types.
Type '{ organizationId?: string | undefined; } | undefined' is not assignable to type 'Record<string, any>'.
Type 'undefined' is not assignable to type 'Record<string, any>'. (ts 2322)
Originally created by @rohovskoi on GitHub (Oct 6, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce To reproduce you will need: ``` organization({ requireEmailVerificationOnInvitation: true, ac: orgAc, creatorRole: 'owner', roles: { owner, admin, customer, }, teams: { enabled: true }, dynamicAccessControl: { maximumRolesPerOrganization: 20, enabled: true } }), ``` ### Current vs. Expected behavior Current: plugin breaks. Expected: types of the organization plugin are inferred and `betterAuth()` client works as expected. ### What version of Better Auth are you using? 1.3.26 ### System info ```bash . ``` ### Which area(s) are affected? (Select all that apply) Package ### Auth config (if applicable) ```typescript ``` ### Additional context Error: ``` Type '{ id: "organization"; endpoints: { createOrganization: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0: { body: { name: string; slug: string; userId?: string | undefined; logo?: string | undefined; metadata?: Record<...> | undefined; keepCurrentActiveOrganization?: boolean | ...' is not assignable to type 'BetterAuthPlugin'. Types of property 'endpoints' are incompatible. Type '{ createOrganization: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0: { body: { name: string; slug: string; userId?: string | undefined; logo?: string | undefined; metadata?: Record<string, any> | undefined; keepCurrentActiveOrganization?: boolean | undefined; }; } & ... 6 m...' is not assignable to type '{ [key: string]: Endpoint; }'. Property 'listOrgRoles' is incompatible with index signature. Type '{ <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: { organizationId?: string | undefined; } | undefined; } & { params?: Record<string, any>; } & { ...; } & { ...; } & { ...; } & { ...; }) | undefined): Promis...' is not assignable to type 'Endpoint'. Type '{ <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: { organizationId?: string | undefined; } | undefined; } & { params?: Record<string, any>; } & { ...; } & { ...; } & { ...; } & { ...; }) | undefined): Promis...' is not assignable to type '{ options: EndpointOptions; path: string; }'. The types of 'options.metadata.$Infer.query' are incompatible between these types. Type '{ organizationId?: string | undefined; } | undefined' is not assignable to type 'Record<string, any>'. Type 'undefined' is not assignable to type 'Record<string, any>'. (ts 2322) ```
GiteaMirror added the plugin label 2026-03-13 09:25:25 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 6, 2025):

This is a known TypeScript type inference issue when enabling dynamicAccessControl in the Organization Plugin. The root cause is stricter TypeScript settings—especially "exactOptionalPropertyTypes": true in your tsconfig.json—which causes type incompatibilities and breaks type inference for the plugin's schema. Disabling this option ("exactOptionalPropertyTypes": false) is a safe workaround, though it does reduce type safety. This workaround is confirmed by both users and maintainers and is currently the best option until a proper fix is released (source).

You may also encounter issues where TypeScript can't infer types for API calls that accept either roleId or roleName (but not both) when dynamic access control is enabled. In these cases, use a type assertion (e.g., as any or as { roleId: string }) when calling these APIs. Runtime validation will still enforce the correct usage, so this is safe in practice (source).

Recent changes to the plugin's schema typing (switching from AuthPluginSchema to BetterAuthPluginDBSchema) may also impact type inference, so keep an eye on future releases for a more robust fix (source).

For now, set "exactOptionalPropertyTypes": false in your tsconfig.json and use type assertions for affected API calls as needed.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Oct 6, 2025): <!-- Answer --> This is a known TypeScript type inference issue when enabling `dynamicAccessControl` in the Organization Plugin. The root cause is stricter TypeScript settings—especially `"exactOptionalPropertyTypes": true` in your `tsconfig.json`—which causes type incompatibilities and breaks type inference for the plugin's schema. Disabling this option (`"exactOptionalPropertyTypes": false`) is a safe workaround, though it does reduce type safety. This workaround is confirmed by both users and maintainers and is currently the best option until a proper fix is released ([source](https://github.com/better-auth/better-auth/issues/2471)). You may also encounter issues where TypeScript can't infer types for API calls that accept either `roleId` or `roleName` (but not both) when dynamic access control is enabled. In these cases, use a type assertion (e.g., `as any` or `as { roleId: string }`) when calling these APIs. Runtime validation will still enforce the correct usage, so this is safe in practice ([source](https://github.com/better-auth/better-auth/issues/4636)). Recent changes to the plugin's schema typing (switching from `AuthPluginSchema` to `BetterAuthPluginDBSchema`) may also impact type inference, so keep an eye on future releases for a more robust fix ([source](https://github.com/better-auth/better-auth/pull/5092)). For now, set `"exactOptionalPropertyTypes": false` in your `tsconfig.json` and use type assertions for affected API calls as needed. <!-- 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/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/36e2f43f-cb9d-4af5-ab58-0e45fa89a1e0?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/5119)
Author
Owner

@rohovskoi commented on GitHub (Oct 6, 2025):

@dosu

I can't set "exactOptionalPropertyTypes": false. Its essential for my use-case.

@rohovskoi commented on GitHub (Oct 6, 2025): @dosu I can't set "exactOptionalPropertyTypes": false. Its essential for my use-case.
Author
Owner

@dosubot[bot] commented on GitHub (Oct 6, 2025):

With "exactOptionalPropertyTypes": true, TypeScript enforces that optional properties can't be undefined when assigned to Record<string, any>. The Organization Plugin's dynamicAccessControl endpoints use optional properties like organizationId?: string | undefined, which breaks this contract and causes the type errors you see.

Recent updates (like PR #5003) improved compatibility by explicitly marking these properties as string | undefined, but the core issue remains: the plugin's endpoint types fundamentally conflict with strict optional property enforcement because of how endpoint contracts are structured in Better Auth. There are no code-level workarounds or config tweaks that resolve this—only type assertions (e.g., as any) or relaxing your TypeScript config will unblock you right now.

If strict optional property types are essential, you'll need to wait for a deeper type system refactor in Better Auth to fully support this use case. There are no safer or more idiomatic workarounds in the current version.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Oct 6, 2025): <!-- Answer --> With "exactOptionalPropertyTypes": true, TypeScript enforces that optional properties can't be undefined when assigned to Record<string, any>. The Organization Plugin's dynamicAccessControl endpoints use optional properties like organizationId?: string | undefined, which breaks this contract and causes the type errors you see. Recent updates (like [PR #5003](https://github.com/better-auth/better-auth/pull/5003)) improved compatibility by explicitly marking these properties as string | undefined, but the core issue remains: the plugin's endpoint types fundamentally conflict with strict optional property enforcement because of how endpoint contracts are structured in Better Auth. There are no code-level workarounds or config tweaks that resolve this—only type assertions (e.g., as any) or relaxing your TypeScript config will unblock you right now. If strict optional property types are essential, you'll need to wait for a deeper type system refactor in Better Auth to fully support this use case. There are no safer or more idiomatic workarounds in the current version. <!-- 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/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/093c24a0-1f7c-4ff8-841e-fb3c60ebe7f5?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/5119)
Author
Owner

@rohovskoi commented on GitHub (Oct 9, 2025):

Is there an update on this issue?

@rohovskoi commented on GitHub (Oct 9, 2025): Is there an update on this issue?
Author
Owner

@rohovskoi commented on GitHub (Oct 15, 2025):

@himself65 Hey 👋🏻

I saw you closed some PRs which were addressing this issue. Have you found a bigger problem or why did you close them?

@rohovskoi commented on GitHub (Oct 15, 2025): @himself65 Hey 👋🏻 I saw you closed some PRs which were addressing this issue. Have you found a bigger problem or why did you close them?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2081