[GH-ISSUE #4804] Issues with exactOptionalPropertyTypes when using server API from organization plugin #10077

Closed
opened 2026-04-13 05:59:25 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @rohovskoi on GitHub (Sep 21, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/4804

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

auth.api.createOrgRole({
                body: {
                  role: payload.roleName,
                  permission: payload.permission,
                  organizationId: payload.organizationId // <------- Doesnt accept:  organizationId: string | undefined
                },
                headers: fromNodeHeaders(req.headers),
            }),
auth.api.updateOrgRole({
                  body: {
                     roleId: payload.roleId,
                     roleName: payload.roleName,
                     data: {
                       roleName: payload.roleName,
                       permission : payload.permission // <------- Doesnt accept:  permission: { [x: string]: string[]; } | undefined
                     }
                  },
                  headers: fromNodeHeaders(req.headers),
              }),
auth.api.deleteOrgRole({
                body: {
                  roleId: payload.roleId,
                  roleName: payload.roleName,
                  organizationId: payload.organizationId // <------- Doesnt accept:  organizationId: string | undefined
                },
                headers: fromNodeHeaders(req.headers),
              }),

Current vs. Expected behavior

Current: Doesn't accept value of type string | undefined.

Expected: all methods where this a type defined as smth?: Sometype, it should accept Sometype | undefined. Otherwise exactOptionalPropertyTypes throw an error.

This API should be the target as it has every type and property properly defined. => auth.api.createOrganization()

What version of Better Auth are you using?

1.3.13

System info

.

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

Backend

Auth config (if applicable)


Additional context

Error example

Type '{ role: string; permission: { [x: string]: string[]; }; organizationId: string | undefined; }' is not assignable to type '{ organizationId?: string; role: string; permission: Record<string, string[]>; } & { additionalFields?: {}; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Type '{ role: string; permission: { [x: string]: string[]; }; organizationId: string | undefined; }' is not assignable to type '{ organizationId?: string; role: string; permission: Record<string, string[]>; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'organizationId' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'. (ts 2375)
Originally created by @rohovskoi on GitHub (Sep 21, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/4804 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce ```ts auth.api.createOrgRole({ body: { role: payload.roleName, permission: payload.permission, organizationId: payload.organizationId // <------- Doesnt accept: organizationId: string | undefined }, headers: fromNodeHeaders(req.headers), }), ``` ```ts auth.api.updateOrgRole({ body: { roleId: payload.roleId, roleName: payload.roleName, data: { roleName: payload.roleName, permission : payload.permission // <------- Doesnt accept: permission: { [x: string]: string[]; } | undefined } }, headers: fromNodeHeaders(req.headers), }), ``` ```ts auth.api.deleteOrgRole({ body: { roleId: payload.roleId, roleName: payload.roleName, organizationId: payload.organizationId // <------- Doesnt accept: organizationId: string | undefined }, headers: fromNodeHeaders(req.headers), }), ``` ### Current vs. Expected behavior Current: Doesn't accept value of type `string | undefined`. Expected: all methods where this a type defined as `smth?: Sometype`, it should accept `Sometype | undefined`. Otherwise exactOptionalPropertyTypes throw an error. ✅ This API should be the target as it has every type and property properly defined. => `auth.api.createOrganization()` ### What version of Better Auth are you using? 1.3.13 ### System info ```bash . ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript ``` ### Additional context Error example ``` Type '{ role: string; permission: { [x: string]: string[]; }; organizationId: string | undefined; }' is not assignable to type '{ organizationId?: string; role: string; permission: Record<string, string[]>; } & { additionalFields?: {}; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Type '{ role: string; permission: { [x: string]: string[]; }; organizationId: string | undefined; }' is not assignable to type '{ organizationId?: string; role: string; permission: Record<string, string[]>; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties. Types of property 'organizationId' are incompatible. Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'. (ts 2375) ```
GiteaMirror added the locked label 2026-04-13 05:59:25 -05:00
Author
Owner

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

This is a known issue with Better Auth v1.3.13 and TypeScript's exactOptionalPropertyTypes setting. The organization plugin's API methods (like createOrgRole, updateOrgRole, deleteOrgRole, and updateOrgRole) define optional properties such as organizationId as organizationId?: string, which means TypeScript will not accept string | undefined—it only allows the property to be omitted entirely, not explicitly set to undefined when exactOptionalPropertyTypes is enabled. This causes the type error you're seeing source.

Recent updates have fixed this by refactoring plugin schemas to avoid spreading undefined values and making the types compatible with exactOptionalPropertyTypes. The recommended solution is to upgrade to a version after PRs #3941, #3947, and #3961 (merged after 2025-08-13). You can install the fixed version directly with:

npm i https://pkg.pr.new/better-auth/better-auth@3961

source

If you can't upgrade right now, you can work around the issue by omitting optional properties when their value is undefined (don't include them in the object at all), or use a type assertion like as any for the problematic property to bypass the error source.

Let me know if you need a code example for the workaround!

To reply, just mention @dosu.


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

<!-- gh-comment-id:3316151548 --> @dosubot[bot] commented on GitHub (Sep 21, 2025): <!-- Answer --> This is a known issue with Better Auth v1.3.13 and TypeScript's `exactOptionalPropertyTypes` setting. The organization plugin's API methods (like `createOrgRole`, `updateOrgRole`, `deleteOrgRole`, and `updateOrgRole`) define optional properties such as `organizationId` as `organizationId?: string`, which means TypeScript will not accept `string | undefined`—it only allows the property to be omitted entirely, not explicitly set to `undefined` when `exactOptionalPropertyTypes` is enabled. This causes the type error you're seeing [`source`](https://github.com/better-auth/better-auth/issues/3570). Recent updates have fixed this by refactoring plugin schemas to avoid spreading undefined values and making the types compatible with `exactOptionalPropertyTypes`. The recommended solution is to upgrade to a version after PRs #3941, #3947, and #3961 (merged after 2025-08-13). You can install the fixed version directly with: ```bash npm i https://pkg.pr.new/better-auth/better-auth@3961 ``` [`source`](https://github.com/better-auth/better-auth/issues/3570) If you can't upgrade right now, you can work around the issue by omitting optional properties when their value is `undefined` (don't include them in the object at all), or use a type assertion like `as any` for the problematic property to bypass the error [`source`](https://github.com/better-auth/better-auth/issues/4463). Let me know if you need a code example for the workaround! <!-- 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/a3148eb1-3189-41a2-b448-50714715e0dc?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/a3148eb1-3189-41a2-b448-50714715e0dc?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/a3148eb1-3189-41a2-b448-50714715e0dc?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/a3148eb1-3189-41a2-b448-50714715e0dc?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/a3148eb1-3189-41a2-b448-50714715e0dc?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/a3148eb1-3189-41a2-b448-50714715e0dc?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/a3148eb1-3189-41a2-b448-50714715e0dc?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/4804)
Author
Owner

@rohovskoi commented on GitHub (Sep 21, 2025):

Nope, its not fixed. I'm on the very latest version. This issue targets server APIs, rather than organization plugin in plugins: [] itself.

<!-- gh-comment-id:3316161483 --> @rohovskoi commented on GitHub (Sep 21, 2025): Nope, its not fixed. I'm on the very latest version. This issue targets server APIs, rather than organization plugin in `plugins: []` itself.
Author
Owner

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

You're right—the issue isn't fixed in the latest version. The organization plugin's API methods still use TypeScript's optional property syntax (e.g., organizationId?: string), which means TypeScript with exactOptionalPropertyTypes enabled won't accept string | undefined—it only allows the property to be omitted entirely, not explicitly set to undefined see code.

Recent PRs improved schema construction but did not change the TypeScript types for API request bodies, so the incompatibility persists see PR #3941. Until the types are updated to explicitly allow T | undefined, you'll need to work around this by omitting optional properties when their value is undefined, or using a type assertion like as any to bypass the error.

Example workaround:

const body: any = {
  role: payload.roleName,
  permission: payload.permission,
  ...(payload.organizationId !== undefined && { organizationId: payload.organizationId })
};
auth.api.createOrgRole({ body, headers: fromNodeHeaders(req.headers) });

Or:

auth.api.createOrgRole({
  body: {
    role: payload.roleName,
    permission: payload.permission,
    organizationId: payload.organizationId as any // type assertion
  },
  headers: fromNodeHeaders(req.headers),
});

If you need a more robust solution, the types would need to be updated repo-wide to support T | undefined for optionals.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3316164998 --> @dosubot[bot] commented on GitHub (Sep 21, 2025): <!-- Answer --> You're right—the issue isn't fixed in the latest version. The organization plugin's API methods still use TypeScript's optional property syntax (e.g., `organizationId?: string`), which means TypeScript with `exactOptionalPropertyTypes` enabled won't accept `string | undefined`—it only allows the property to be omitted entirely, not explicitly set to `undefined` [see code](https://github.com/better-auth/better-auth/blob/1baaccbf52803d27046dce3da57c5c7201a020fb/packages/better-auth/src/plugins/organization/routes/crud-access-control.ts). Recent PRs improved schema construction but did not change the TypeScript types for API request bodies, so the incompatibility persists [see PR #3941](https://github.com/better-auth/better-auth/pull/3941). Until the types are updated to explicitly allow `T | undefined`, you'll need to work around this by omitting optional properties when their value is `undefined`, or using a type assertion like `as any` to bypass the error. Example workaround: ```ts const body: any = { role: payload.roleName, permission: payload.permission, ...(payload.organizationId !== undefined && { organizationId: payload.organizationId }) }; auth.api.createOrgRole({ body, headers: fromNodeHeaders(req.headers) }); ``` Or: ```ts auth.api.createOrgRole({ body: { role: payload.roleName, permission: payload.permission, organizationId: payload.organizationId as any // type assertion }, headers: fromNodeHeaders(req.headers), }); ``` If you need a more robust solution, the types would need to be updated repo-wide to support `T | undefined` for optionals. <!-- 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/74f3a097-114d-4434-ab7b-07a3ffd8939d?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/74f3a097-114d-4434-ab7b-07a3ffd8939d?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/74f3a097-114d-4434-ab7b-07a3ffd8939d?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/74f3a097-114d-4434-ab7b-07a3ffd8939d?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/74f3a097-114d-4434-ab7b-07a3ffd8939d?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/74f3a097-114d-4434-ab7b-07a3ffd8939d?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/74f3a097-114d-4434-ab7b-07a3ffd8939d?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/4804)
Author
Owner

@himself65 commented on GitHub (Sep 23, 2025):

Hi, I've seen some issues with exactOptionalPropertyTypes. I'm trying to fix this

<!-- gh-comment-id:3321965919 --> @himself65 commented on GitHub (Sep 23, 2025): Hi, I've seen some issues with `exactOptionalPropertyTypes`. I'm trying to fix this
Author
Owner

@rohovskoi commented on GitHub (Sep 23, 2025):

@himself65 awesome! Thank you 💪

<!-- gh-comment-id:3325462976 --> @rohovskoi commented on GitHub (Sep 23, 2025): @himself65 awesome! Thank you 💪
Author
Owner

@artimath commented on GitHub (Sep 27, 2025):

Not sure if this fixes it, but I was able to cleanly fix all the types while keeping exactOptionalPropertyTypes enabled:

https://github.com/better-auth/better-auth/pull/4930#issuecomment-3341677893

import {
  adminClient,
  emailOTPClient,
  inferAdditionalFields,
  magicLinkClient,
  multiSessionClient,
  organizationClient,
  passkeyClient,
  twoFactorClient,
} from "better-auth/client/plugins";
import { InferAuth } from "better-auth/client";
import { createAuthClient } from "better-auth/react";
import { env } from "@/lib/env.client";
import { ac, admin as adminRole, superadmin as superAdminRole, user as userRole } from "./permissions";
import type { auth } from "@/lib/auth/auth";

export const authClient = createAuthClient({
  baseURL: env.VITE_SERVER_URL,
  plugins: [
    inferAdditionalFields<typeof auth>(),
    twoFactorClient(),
    passkeyClient(),
    adminClient({
      ac,
      roles: {
        user: userRole,
        admin: adminRole,
        superadmin: superAdminRole,
      },
    }),
    organizationClient(),
    emailOTPClient(),
    magicLinkClient(),
    multiSessionClient(),
  ],
  $InferAuth: InferAuth<typeof auth>(), <--- magic fix?
});

export type AuthClient = typeof authClient;
export type Session = AuthClient["$Infer"]["Session"];
export type SessionUser = Session["user"];
export type Organization = AuthClient["$Infer"]["Organization"];
export type ActiveOrganization = AuthClient["$Infer"]["ActiveOrganization"];

fix + repro branch/commit for the curious, hope its real, hope it helps!

https://github.com/artimath/tanstack-effect-cloudflare-template/pull/1

<!-- gh-comment-id:3341685407 --> @artimath commented on GitHub (Sep 27, 2025): Not sure if this fixes it, but I was able to cleanly fix all the types while keeping exactOptionalPropertyTypes enabled: https://github.com/better-auth/better-auth/pull/4930#issuecomment-3341677893 ```ts import { adminClient, emailOTPClient, inferAdditionalFields, magicLinkClient, multiSessionClient, organizationClient, passkeyClient, twoFactorClient, } from "better-auth/client/plugins"; import { InferAuth } from "better-auth/client"; import { createAuthClient } from "better-auth/react"; import { env } from "@/lib/env.client"; import { ac, admin as adminRole, superadmin as superAdminRole, user as userRole } from "./permissions"; import type { auth } from "@/lib/auth/auth"; export const authClient = createAuthClient({ baseURL: env.VITE_SERVER_URL, plugins: [ inferAdditionalFields<typeof auth>(), twoFactorClient(), passkeyClient(), adminClient({ ac, roles: { user: userRole, admin: adminRole, superadmin: superAdminRole, }, }), organizationClient(), emailOTPClient(), magicLinkClient(), multiSessionClient(), ], $InferAuth: InferAuth<typeof auth>(), <--- magic fix? }); export type AuthClient = typeof authClient; export type Session = AuthClient["$Infer"]["Session"]; export type SessionUser = Session["user"]; export type Organization = AuthClient["$Infer"]["Organization"]; export type ActiveOrganization = AuthClient["$Infer"]["ActiveOrganization"]; ``` fix + repro branch/commit for the curious, hope its real, hope it helps! https://github.com/artimath/tanstack-effect-cloudflare-template/pull/1
Author
Owner

@rohovskoi commented on GitHub (Sep 30, 2025):

@artimath thanks for your suggestion! I wish I could use it! Unfortunately, I can't as I implement better-auth mainly server side.

<!-- gh-comment-id:3353827237 --> @rohovskoi commented on GitHub (Sep 30, 2025): @artimath thanks for your suggestion! I wish I could use it! Unfortunately, I can't as I implement better-auth mainly server side.
Author
Owner

@rohovskoi commented on GitHub (Sep 30, 2025):

@himself65 Hey, how are you doing? Did you try fixing it? Would you need more input from our side?

auth.api.updateOrganization() has the same issue.

<!-- gh-comment-id:3353829028 --> @rohovskoi commented on GitHub (Sep 30, 2025): @himself65 Hey, how are you doing? Did you try fixing it? Would you need more input from our side? `auth.api.updateOrganization()` has the same issue.
Author
Owner

@himself65 commented on GitHub (Sep 30, 2025):

@himself65 Hey, how are you doing? Did you try fixing it? Would you need more input from our side?

Hi, I will add a fix for those APIs right now. Changing all of them is a big task

<!-- gh-comment-id:3353832799 --> @himself65 commented on GitHub (Sep 30, 2025): > [@himself65](https://github.com/himself65) Hey, how are you doing? Did you try fixing it? Would you need more input from our side? Hi, I will add a fix for those APIs right now. Changing all of them is a big task
Author
Owner

@rohovskoi commented on GitHub (Sep 30, 2025):

@himself65 Got it. Thanks and appreciate it! Lmk if you need anything

<!-- gh-comment-id:3353884411 --> @rohovskoi commented on GitHub (Sep 30, 2025): @himself65 Got it. Thanks and appreciate it! Lmk if you need anything
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10077