Argument type for role edit and deletion not inferred #1919

Closed
opened 2026-03-13 09:12:41 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @TheUntraceable on GitHub (Sep 13, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

https://codesandbox.io/p/devbox/sypnpz

  • Configure Organization plugin with dynamicAccessControl.enabled as true on both client and server.
  • Try to delete or edit a role; TS complains about roleId nor roleName existing in that type.

Current vs. Expected behavior

Current: TS complains about roleName nor roleId being specified as a key of the parameter passed to updateRole/deleteRole
Expected: Either argument being accepted, maybe an error if BOTH are specified.

What version of Better Auth are you using?

1.3.9

System info

That command returned nothing.
Better Auth Version: 1.3.9
Typescript Version: 5.4.3

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

Client, Backend, Types

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins/organization";


export const auth = betterAuth({
    plugins: [
        organization({
            dynamicAccessControl: {
                enabled: true
            }
        })
    ]
});

Additional context

No response

Originally created by @TheUntraceable on GitHub (Sep 13, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce https://codesandbox.io/p/devbox/sypnpz - Configure Organization plugin with dynamicAccessControl.enabled as true on both client and server. - Try to delete or edit a role; TS complains about roleId nor roleName existing in that type. ### Current vs. Expected behavior Current: TS complains about roleName nor roleId being specified as a key of the parameter passed to updateRole/deleteRole Expected: Either argument being accepted, maybe an error if BOTH are specified. ### What version of Better Auth are you using? 1.3.9 ### System info ```bash That command returned nothing. Better Auth Version: 1.3.9 Typescript Version: 5.4.3 ``` ### Which area(s) are affected? (Select all that apply) Client, Backend, Types ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { organization } from "better-auth/plugins/organization"; export const auth = betterAuth({ plugins: [ organization({ dynamicAccessControl: { enabled: true } }) ] }); ``` ### Additional context _No response_
GiteaMirror added the plugin label 2026-03-13 09:12:41 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 13, 2025):

This is a known TypeScript limitation when using Better Auth's organization plugin with dynamicAccessControl.enabled: true. The API for updating and deleting roles is designed to accept either a roleId or a roleName (but not both), and this is enforced at both the type and runtime level using zod schemas in the codebase. The public API and tests confirm that either argument works as intended, and passing both is not allowed (sourcetest source).

However, TypeScript can't always infer these types correctly in user code, especially with dynamic roles or when types are surfaced through wrappers like tRPC. The recommended workaround is to use a type assertion (e.g., as any or as { roleId: string }) when calling these APIs (issue reference). For example:

await auth.api.deleteOrgRole({ body: { roleId } as any, headers });
await auth.api.updateOrgRole({ body: { roleName, data: { ... } } as any, headers });

There's currently no safer or more idiomatic workaround in Better Auth 1.3.9. This is documented in issues and confirmed by recent PRs (PR #4087, PR #4337). Runtime validation will still ensure only one of roleId or roleName is accepted, so type assertion is safe in this context.

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 (Sep 13, 2025): <!-- Answer --> This is a known TypeScript limitation when using Better Auth's organization plugin with `dynamicAccessControl.enabled: true`. The API for updating and deleting roles is designed to accept either a `roleId` or a `roleName` (but not both), and this is enforced at both the type and runtime level using zod schemas in the codebase. The public API and tests confirm that either argument works as intended, and passing both is not allowed ([source](https://github.com/better-auth/better-auth/blob/580e681989950e04d6dcd8bfc60de2318c28e7be/packages/better-auth/src/plugins/organization/routes/crud-access-control.ts), [test source](https://github.com/better-auth/better-auth/blob/580e681989950e04d6dcd8bfc60de2318c28e7be/packages/better-auth/src/plugins/organization/routes/crud-access-control.test.ts)). However, TypeScript can't always infer these types correctly in user code, especially with dynamic roles or when types are surfaced through wrappers like tRPC. The recommended workaround is to use a type assertion (e.g., `as any` or `as { roleId: string }`) when calling these APIs ([issue reference](https://github.com/better-auth/better-auth/issues/4463)). For example: ```ts await auth.api.deleteOrgRole({ body: { roleId } as any, headers }); await auth.api.updateOrgRole({ body: { roleName, data: { ... } } as any, headers }); ``` There's currently no safer or more idiomatic workaround in Better Auth 1.3.9. This is documented in issues and confirmed by recent PRs ([PR #4087](https://github.com/better-auth/better-auth/pull/4087), [PR #4337](https://github.com/better-auth/better-auth/pull/4337)). Runtime validation will still ensure only one of `roleId` or `roleName` is accepted, so type assertion is safe in this context. <!-- 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/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/e8024003-cbd6-40b5-92b5-b457b0f7ebdf?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/4636)
Author
Owner

@ping-maxwell commented on GitHub (Oct 1, 2025):

@TheUntraceable Can you confirm that it will require at least 1 option to be required? (not at my PC to test rn)

If you simply want TS to complain if two/both options provided then I don't think that's possible since TS allows for union object keys being used in combination

@ping-maxwell commented on GitHub (Oct 1, 2025): @TheUntraceable Can you confirm that it will require at least 1 option to be required? (not at my PC to test rn) If you simply want TS to complain **if** two/both options provided then I don't think that's possible since TS allows for union object keys being used in combination
Author
Owner

@TheUntraceable commented on GitHub (Oct 7, 2025):

@ping-maxwell Hi there, I specified one of them and it is still complaining. I'll drop the error to you below:
Object literal may only specify known properties, and 'roleName' does not exist in type 'Prettify<({ organizationId?: string | undefined; data: { permission?: Record<string, string[]> | undefined; roleName?: string | undefined; } & Partial<{}>; } & ({ roleName: string; } | { ...; })) & { ...; }>'.ts(2353)

const response = await authClient.organization.updateRole({
    roleName: role.role,
    data: {
        roleName: data.name.trim(),
        permission: permissionsRecord,
    },
});

The type of the args are not inferred and visible in my intelisense.

Image
@TheUntraceable commented on GitHub (Oct 7, 2025): @ping-maxwell Hi there, I specified one of them and it is still complaining. I'll drop the error to you below: Object literal may only specify known properties, and 'roleName' does not exist in type 'Prettify<({ organizationId?: string | undefined; data: { permission?: Record<string, string[]> | undefined; roleName?: string | undefined; } & Partial<{}>; } & ({ roleName: string; } | { ...; })) & { ...; }>'.ts(2353) ```ts const response = await authClient.organization.updateRole({ roleName: role.role, data: { roleName: data.name.trim(), permission: permissionsRecord, }, }); ``` The type of the args are not inferred and visible in my intelisense. <img width="1291" height="442" alt="Image" src="https://github.com/user-attachments/assets/c466d0d5-8f7d-452d-8e0b-5abb3f2baed2" />
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1919