[GH-ISSUE #5122] AdditionalFields break organization plugin and auth api #27492

Closed
opened 2026-04-17 18:32:45 -05:00 by GiteaMirror · 25 comments
Owner

Originally created by @ghassenbenghorbal on GitHub (Oct 6, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/5122

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Add organization plugin with additionalFields

Current vs. Expected behavior

I should be able to use createOrganization but because I'm using additionalFields in the organization typescript says createOrganization does not exist

What version of Better Auth are you using?

1.3.26

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 11 Pro",
    "release": "10.0.26100",
    "cpuCount": 12,
    "cpuModel": "AMD Ryzen 5 5600X 6-Core Processor             ",
    "totalMemory": "15.91 GB",
    "freeMemory": "4.04 GB"
  },
  "node": {
    "version": "v22.16.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.2.0"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null
  }
}

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

Types

Auth config (if applicable)

import { db } from "@repo/db";
import { corsOptions } from "@repo/types";
import { type BetterAuthOptions, betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import {
  admin,
  lastLoginMethod,
  type OrganizationOptions,
  openAPI,
  organization,
  twoFactor,
} from "better-auth/plugins";

const schema = {
  organization: {
    additionalFields: {
      type: {
        type: "string",
        required: true,
        fieldName: "type",
        input: true,
        returned: true,
      },
      locationId: {
        type: "string",
        fieldName: "location_id",
        required: false,
        input: true,
        references: {
          field: "id",
          model: "locations",
          onDelete: "set null",
        },
        returned: true,
      },
    },
  },
  team: {
    additionalFields: {
      type: {
        type: "string",
        required: true,
        fieldName: "type",
        input: true,
        returned: true,
      },
      teamManagerId: {
        type: "string",
        fieldName: "team_manager_id",
        required: true,
        input: true,
        returned: true,
      },
    },
  },
} satisfies NonNullable<OrganizationOptions["schema"]>;

const organizationConfig = {
  membershipLimit: 100, // Optional
  teams: {
    enabled: true,
    maximumTeams: 10, // Optional: limit teams per organization
    maximumMembersPerTeam: 10, // Optional: limit members per team
    allowRemovingAllTeams: false, // Optional: prevent removing the last team
  },
  schema,
} satisfies OrganizationOptions;

type AuthPlugins = [
  ReturnType<typeof organization<typeof organizationConfig>>,
  ReturnType<typeof admin>,
  ReturnType<typeof openAPI>,
  ReturnType<typeof lastLoginMethod>,
  ReturnType<typeof twoFactor>,
];

const plugins: AuthPlugins = [
  organization(organizationConfig),
  admin(),
  openAPI(),
  lastLoginMethod(),
  twoFactor(),
] satisfies AuthPlugins;

const authConfig = {
  appName: "Lanci",
  emailAndPassword: {
    enabled: true,
  },
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60, // Cache duration in seconds
    },
  },
  plugins,
  database: drizzleAdapter(db, {
    provider: "pg",
  }),
  trustedOrigins: corsOptions.origin,
  advanced: {
    useSecureCookies: process.env.NODE_ENV === "production",
  },
} satisfies BetterAuthOptions;

export const auth = betterAuth(authConfig) as ReturnType<
  typeof betterAuth<typeof authConfig>
>;

Additional context

Property 'createOrganization' does not exist on type 'InferAPI<{ readonly ok: { (inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: Record<string, any> | undefined; } & { params?: Record<string, any> | undefined; } & { ...; } & { ...; } & { ...; } & { ...; }) | und...'.ts(2339)

const orgResult = await auth.api.createOrganization({ body: { name: data.organizationName, slug, type: "delivery", metadata: JSON.stringify({ phone: data.phone, website: data.website, address: { street: data.streetAddress, city: data.city, state: data.state, postalCode: data.postalCode, country: data.country, }, }), }, headers, });

Originally created by @ghassenbenghorbal on GitHub (Oct 6, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/5122 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Add organization plugin with additionalFields ### Current vs. Expected behavior I should be able to use createOrganization but because I'm using additionalFields in the organization typescript says createOrganization does not exist ### What version of Better Auth are you using? 1.3.26 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 11 Pro", "release": "10.0.26100", "cpuCount": 12, "cpuModel": "AMD Ryzen 5 5600X 6-Core Processor ", "totalMemory": "15.91 GB", "freeMemory": "4.04 GB" }, "node": { "version": "v22.16.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.2.0" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Types ### Auth config (if applicable) ```typescript import { db } from "@repo/db"; import { corsOptions } from "@repo/types"; import { type BetterAuthOptions, betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { admin, lastLoginMethod, type OrganizationOptions, openAPI, organization, twoFactor, } from "better-auth/plugins"; const schema = { organization: { additionalFields: { type: { type: "string", required: true, fieldName: "type", input: true, returned: true, }, locationId: { type: "string", fieldName: "location_id", required: false, input: true, references: { field: "id", model: "locations", onDelete: "set null", }, returned: true, }, }, }, team: { additionalFields: { type: { type: "string", required: true, fieldName: "type", input: true, returned: true, }, teamManagerId: { type: "string", fieldName: "team_manager_id", required: true, input: true, returned: true, }, }, }, } satisfies NonNullable<OrganizationOptions["schema"]>; const organizationConfig = { membershipLimit: 100, // Optional teams: { enabled: true, maximumTeams: 10, // Optional: limit teams per organization maximumMembersPerTeam: 10, // Optional: limit members per team allowRemovingAllTeams: false, // Optional: prevent removing the last team }, schema, } satisfies OrganizationOptions; type AuthPlugins = [ ReturnType<typeof organization<typeof organizationConfig>>, ReturnType<typeof admin>, ReturnType<typeof openAPI>, ReturnType<typeof lastLoginMethod>, ReturnType<typeof twoFactor>, ]; const plugins: AuthPlugins = [ organization(organizationConfig), admin(), openAPI(), lastLoginMethod(), twoFactor(), ] satisfies AuthPlugins; const authConfig = { appName: "Lanci", emailAndPassword: { enabled: true, }, session: { cookieCache: { enabled: true, maxAge: 5 * 60, // Cache duration in seconds }, }, plugins, database: drizzleAdapter(db, { provider: "pg", }), trustedOrigins: corsOptions.origin, advanced: { useSecureCookies: process.env.NODE_ENV === "production", }, } satisfies BetterAuthOptions; export const auth = betterAuth(authConfig) as ReturnType< typeof betterAuth<typeof authConfig> >; ``` ### Additional context Property 'createOrganization' does not exist on type 'InferAPI<{ readonly ok: { <AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({ body?: undefined; } & { method?: "GET" | undefined; } & { query?: Record<string, any> | undefined; } & { params?: Record<string, any> | undefined; } & { ...; } & { ...; } & { ...; } & { ...; }) | und...'.ts(2339) `const orgResult = await auth.api.createOrganization({ body: { name: data.organizationName, slug, type: "delivery", metadata: JSON.stringify({ phone: data.phone, website: data.website, address: { street: data.streetAddress, city: data.city, state: data.state, postalCode: data.postalCode, country: data.country, }, }), }, headers, });`
GiteaMirror added the locked label 2026-04-17 18:32:45 -05:00
Author
Owner

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

This is a known TypeScript inference issue in Better Auth v1.3.26 when using additionalFields in the organization plugin. The root cause is incompatibility between the plugin's endpoint types and strict TypeScript settings, especially "exactOptionalPropertyTypes": true in your tsconfig.json. Disabling this option restores the expected typings and makes createOrganization available, but reduces type safety [source] [source].

If you need to keep strict optional property types, there are currently no safe or idiomatic workarounds—only type assertions (like as any) or relaxing your TypeScript config will unblock usage until Better Auth refactors its type system [source].

Explicitly typing your plugins array with ReturnType<typeof plugin> for each plugin can help TypeScript infer API methods like createOrganization [source]. Placing plugins inline in the config sometimes works, but may cause other typing issues or type bloat errors.

If your client and server are in separate repos, you must manually duplicate and keep the organization additionalFields schema in sync on both sides to avoid validation errors and API issues. Better Auth does not currently provide a built-in way to sync organization schemas across repos; inferAdditionalFields only works for user/session fields, not organizations [source].

As of now, additionalFields are not fully supported for organizations in Better Auth. Maintainers suggest using the metadata field or a custom endpoint as a workaround for storing extra organization data [source].

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:3372275571 --> @dosubot[bot] commented on GitHub (Oct 6, 2025): <!-- Answer --> This is a known TypeScript inference issue in Better Auth v1.3.26 when using additionalFields in the organization plugin. The root cause is incompatibility between the plugin's endpoint types and strict TypeScript settings, especially `"exactOptionalPropertyTypes": true` in your tsconfig.json. Disabling this option restores the expected typings and makes `createOrganization` available, but reduces type safety [[source]](https://github.com/better-auth/better-auth/issues/2471) [[source]](https://github.com/better-auth/better-auth/issues/5119). If you need to keep strict optional property types, there are currently no safe or idiomatic workarounds—only type assertions (like `as any`) or relaxing your TypeScript config will unblock usage until Better Auth refactors its type system [[source]](https://github.com/better-auth/better-auth/issues/5119). Explicitly typing your plugins array with `ReturnType<typeof plugin>` for each plugin can help TypeScript infer API methods like `createOrganization` [[source]](https://github.com/better-auth/better-auth/issues/3408). Placing plugins inline in the config sometimes works, but may cause other typing issues or type bloat errors. If your client and server are in separate repos, you must manually duplicate and keep the organization additionalFields schema in sync on both sides to avoid validation errors and API issues. Better Auth does not currently provide a built-in way to sync organization schemas across repos; `inferAdditionalFields` only works for user/session fields, not organizations [[source]](https://github.com/better-auth/better-auth/issues/3500). As of now, additionalFields are not fully supported for organizations in Better Auth. Maintainers suggest using the `metadata` field or a custom endpoint as a workaround for storing extra organization data [[source]](https://github.com/better-auth/better-auth/issues/1420). <!-- 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/6f6e69ed-bb21-4166-a8d3-a79635cfce04?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/6f6e69ed-bb21-4166-a8d3-a79635cfce04?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/6f6e69ed-bb21-4166-a8d3-a79635cfce04?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/6f6e69ed-bb21-4166-a8d3-a79635cfce04?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/6f6e69ed-bb21-4166-a8d3-a79635cfce04?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/6f6e69ed-bb21-4166-a8d3-a79635cfce04?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/6f6e69ed-bb21-4166-a8d3-a79635cfce04?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/5122)
Author
Owner

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

  1. I don't have "exactOptionalPropertyTypes": true
  2. The organization plugin does support additionalFields https://www.better-auth.com/docs/plugins/organization#additional-fields
<!-- gh-comment-id:3372306601 --> @ghassenbenghorbal commented on GitHub (Oct 6, 2025): 1. I don't have "exactOptionalPropertyTypes": true 2. The organization plugin does support additionalFields [https://www.better-auth.com/docs/plugins/organization#additional-fields](https://www.better-auth.com/docs/plugins/organization#additional-fields)
Author
Owner

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

You're right—the organization plugin does support additionalFields, and the docs are accurate. The issue with createOrganization missing from auth.api when using additionalFields is a TypeScript inference problem, not a lack of support in the plugin code. This usually happens if the plugins array isn't typed as a tuple of ReturnType for each plugin, or if the config isn't using satisfies BetterAuthOptions. Type bloat from complex additionalFields can also break inference and cause API methods to disappear from the types (example).

Your config looks correct, but if you're still seeing the error, try these steps:

  • Make sure your plugins array is explicitly typed as a tuple of ReturnType for each plugin.
  • Use satisfies BetterAuthOptions for your config object.
  • If you have declaration: true in your tsconfig, try setting declaration: false to avoid type bloat issues.
  • If possible, upgrade to a version after PR #3941, which improved schema merging and type inference for additionalFields (PR #3941).

If you're in a monorepo or have client/server in separate repos, make sure the organization additionalFields schema is exactly the same on both sides—manual sync is required for organizations (reference).

If none of these work, it's likely a TypeScript type bloat edge case. You can use a type assertion (as any) as a temporary workaround until the type system is further improved (reference).

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:3372324809 --> @dosubot[bot] commented on GitHub (Oct 6, 2025): <!-- Answer --> You're right—the organization plugin does support additionalFields, and the docs are accurate. The issue with createOrganization missing from auth.api when using additionalFields is a TypeScript inference problem, not a lack of support in the plugin code. This usually happens if the plugins array isn't typed as a tuple of ReturnType<typeof plugin> for each plugin, or if the config isn't using satisfies BetterAuthOptions. Type bloat from complex additionalFields can also break inference and cause API methods to disappear from the types ([example](https://github.com/better-auth/better-auth/issues/3408)). Your config looks correct, but if you're still seeing the error, try these steps: - Make sure your plugins array is explicitly typed as a tuple of ReturnType<typeof plugin> for each plugin. - Use satisfies BetterAuthOptions for your config object. - If you have declaration: true in your tsconfig, try setting declaration: false to avoid type bloat issues. - If possible, upgrade to a version after PR #3941, which improved schema merging and type inference for additionalFields ([PR #3941](https://github.com/better-auth/better-auth/pull/3941)). If you're in a monorepo or have client/server in separate repos, make sure the organization additionalFields schema is exactly the same on both sides—manual sync is required for organizations ([reference](https://github.com/better-auth/better-auth/issues/3500)). If none of these work, it's likely a TypeScript type bloat edge case. You can use a type assertion (as any) as a temporary workaround until the type system is further improved ([reference](https://github.com/better-auth/better-auth/issues/3408)). <!-- 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/fd4d6988-397f-41f0-a27c-452915cd478b?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/fd4d6988-397f-41f0-a27c-452915cd478b?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/fd4d6988-397f-41f0-a27c-452915cd478b?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/fd4d6988-397f-41f0-a27c-452915cd478b?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/fd4d6988-397f-41f0-a27c-452915cd478b?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/fd4d6988-397f-41f0-a27c-452915cd478b?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/fd4d6988-397f-41f0-a27c-452915cd478b?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/5122)
Author
Owner

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

  1. My plugins array is explicitly typed as a tuple of ReturnType for each plugin.
  2. I am using satisfies BetterAuthOptions for my config object.
  3. turning off declaration does not fix the issue
  4. I have the latest version of better auth
<!-- gh-comment-id:3372439363 --> @ghassenbenghorbal commented on GitHub (Oct 6, 2025): 1. My plugins array is explicitly typed as a tuple of ReturnType for each plugin. 2. I am using satisfies BetterAuthOptions for my config object. 3. turning off declaration does not fix the issue 4. I have the latest version of better auth
Author
Owner

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

same problem here, after updating to 1.3.26 the organization additional fields types break, everything worked fine until the version update.

...

  node_modules/better-call/dist/router-DcqXHY8X.d.ts:393:5
    393     body: Body;
            ~~~~
    The expected type comes from property 'body' which is declared here on type '{ body: ({ name: string; email: string; password: string; image?: string | undefined; callbackURL?: string | undefined; rememberMe?: boolean | undefined; } | ({ name: string; email: string; password: string; image?: string | undefined; callbackURL?: string | undefined; rememberMe?: boolean | undefined; } & {} & {}))...'

[8:32:10 PM] Found 9 errors. Watching for file changes.

(all 9 errors are organization related types errors)

  • Organization additional fields schema snippet
 schema: {
    organization: {
      additionalFields: {
        identityNumber: {
          type: "string",
        },
        registrationNumber: {
          type: "string",
        },
        address: {
          type: "string",
        },
        email: {
          type: "string",
        },
        phone: {
          type: "string",
        },
        subscriptionPlanId: {
          type: "string",
        },
      },
    },
<!-- gh-comment-id:3373012150 --> @RawkFX commented on GitHub (Oct 6, 2025): same problem here, after updating to 1.3.26 the organization additional fields types break, everything worked fine until the version update. ``` ... node_modules/better-call/dist/router-DcqXHY8X.d.ts:393:5 393 body: Body; ~~~~ The expected type comes from property 'body' which is declared here on type '{ body: ({ name: string; email: string; password: string; image?: string | undefined; callbackURL?: string | undefined; rememberMe?: boolean | undefined; } | ({ name: string; email: string; password: string; image?: string | undefined; callbackURL?: string | undefined; rememberMe?: boolean | undefined; } & {} & {}))...' [8:32:10 PM] Found 9 errors. Watching for file changes. ``` (all 9 errors are organization related types errors) - Organization additional fields schema snippet ``` schema: { organization: { additionalFields: { identityNumber: { type: "string", }, registrationNumber: { type: "string", }, address: { type: "string", }, email: { type: "string", }, phone: { type: "string", }, subscriptionPlanId: { type: "string", }, }, }, ```
Author
Owner

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

My types are messed up. Session and user types are different from the actual data returned. Everything works perfectly fine the moment I remove additionalFields and have the auth options object as a whole directly in betterAuth()

<!-- gh-comment-id:3373221542 --> @ghassenbenghorbal commented on GitHub (Oct 6, 2025): My types are messed up. Session and user types are different from the actual data returned. Everything works perfectly fine the moment I remove additionalFields and have the auth options object as a whole directly in betterAuth()
Author
Owner

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

@ghassenbenghorbal

export const auth = betterAuth(authConfig) as ReturnType<
typeof betterAuth
;

type asserting breaks type inference 9 times out of 10 avoid doing this if possible

Most cases people do this because ts is complaining, can you provide your tsconfig?

<!-- gh-comment-id:3375484463 --> @ping-maxwell commented on GitHub (Oct 7, 2025): @ghassenbenghorbal > export const auth = betterAuth(authConfig) as ReturnType< typeof betterAuth<typeof authConfig> >; type asserting breaks type inference 9 times out of 10 avoid doing this if possible Most cases people do this because ts is complaining, can you provide your tsconfig?
Author
Owner

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

@ping-maxwell
Typescript makes me assert the type the moment I introduce additionalFields to the equation. It doesn't work otherwise.
My project is a turborepo pnpm monorepo with this being the base tsconfig:

  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "composite": false,
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "allowImportingTsExtensions": true,
    "inlineSources": false,
    "isolatedModules": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "preserveWatchOutput": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "strictNullChecks": true
  },
  "exclude": ["node_modules"]
}
<!-- gh-comment-id:3376250151 --> @ghassenbenghorbal commented on GitHub (Oct 7, 2025): @ping-maxwell Typescript makes me assert the type the moment I introduce additionalFields to the equation. It doesn't work otherwise. My project is a turborepo pnpm monorepo with this being the base tsconfig: ```{ "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "composite": false, "declaration": true, "declarationMap": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "allowImportingTsExtensions": true, "inlineSources": false, "isolatedModules": true, "module": "ESNext", "moduleResolution": "bundler", "noUnusedLocals": false, "noUnusedParameters": false, "preserveWatchOutput": true, "skipLibCheck": true, "strict": true, "noEmit": true, "strictNullChecks": true }, "exclude": ["node_modules"] }
Author
Owner

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

disable declaration and declarationMap and things should work

<!-- gh-comment-id:3376536588 --> @ping-maxwell commented on GitHub (Oct 7, 2025): disable `declaration` and `declarationMap` and things should work
Author
Owner

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

Same issue. when I build the error reappears:
src/auth.ts(113,14): error TS2742: The inferred type of 'auth' cannot be named without a reference to '../node_modules/better-auth/dist/shared/better-auth.B4JGLmcI'. This is likely not portable. A type annotation is necessary.

<!-- gh-comment-id:3377307462 --> @ghassenbenghorbal commented on GitHub (Oct 7, 2025): Same issue. when I build the error reappears: `src/auth.ts(113,14): error TS2742: The inferred type of 'auth' cannot be named without a reference to '../node_modules/better-auth/dist/shared/better-auth.B4JGLmcI'. This is likely not portable. A type annotation is necessary.`
Author
Owner

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

I just tested your tsconfig on my end, I see the exact error, but the moment I set declaration and declarationMap to false things work for me. 🤔

Could it be that you're in a monorepo with potentially other conflicting tsconfigs?

<!-- gh-comment-id:3382069915 --> @ping-maxwell commented on GitHub (Oct 8, 2025): I just tested your tsconfig on my end, I see the exact error, but the moment I set `declaration` and `declarationMap` to false things work for me. 🤔 Could it be that you're in a monorepo with potentially other conflicting tsconfigs?
Author
Owner

@ghassenbenghorbal commented on GitHub (Oct 8, 2025):

I just tested your tsconfig on my end, I see the exact error, but the moment I set declaration and declarationMap to false things work for me. 🤔

Have you built the project ?

Could it be that you're in a monorepo with potentially other conflicting tsconfigs?

I turned off declaration and declarationMap globally

<!-- gh-comment-id:3382844321 --> @ghassenbenghorbal commented on GitHub (Oct 8, 2025): > I just tested your tsconfig on my end, I see the exact error, but the moment I set `declaration` and `declarationMap` to false things work for me. 🤔 Have you built the project ? > Could it be that you're in a monorepo with potentially other conflicting tsconfigs? I turned off declaration and declarationMap globally
Author
Owner

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

in v1.3.27 by adding in the auth file the as ReturnType<typeof betterAuth> it resolved my issue

as in

export default auth as ReturnType<typeof betterAuth>

it worked without setting the declaration and declarationMap to false.

Framework: Nest.JS 11
Better-Auth: 1.3.27

<!-- gh-comment-id:3385769582 --> @RawkFX commented on GitHub (Oct 9, 2025): in v1.3.27 by adding in the auth file the ```as ReturnType<typeof betterAuth>``` it resolved my issue as in ``` export default auth as ReturnType<typeof betterAuth> ``` it worked without setting the declaration and declarationMap to false. Framework: Nest.JS 11 Better-Auth: 1.3.27
Author
Owner

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

in v1.3.27 by adding in the auth file the as ReturnType<typeof betterAuth> it resolved my issue

as in

export default auth as ReturnType<typeof betterAuth>

it worked without setting the declaration and declarationMap to false.

Framework: Nest.JS 11 Better-Auth: 1.3.27

Yeah that's what I'm doing already as you can see in the code I included above but the issue is that you lose typing for plugins when you do so

<!-- gh-comment-id:3386062241 --> @ghassenbenghorbal commented on GitHub (Oct 9, 2025): > in v1.3.27 by adding in the auth file the `as ReturnType<typeof betterAuth>` it resolved my issue > > as in > > ``` > export default auth as ReturnType<typeof betterAuth> > ``` > > it worked without setting the declaration and declarationMap to false. > > Framework: Nest.JS 11 Better-Auth: 1.3.27 Yeah that's what I'm doing already as you can see in the code I included above but the issue is that you lose typing for plugins when you do so
Author
Owner

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

Have you built the project ?

Most of everyone who uses Better-Auth right now has declaration disabled, since it's not supported yet. Either this or force casting a return type to the auth config which is very much discouraged.

Unless your build system requires declaration you shouldn't run into build issue regarding declaration

<!-- gh-comment-id:3390157463 --> @ping-maxwell commented on GitHub (Oct 10, 2025): > Have you built the project ? Most of everyone who uses Better-Auth right now has declaration disabled, since it's not supported yet. Either this or force casting a return type to the auth config which is very much discouraged. Unless your build system requires declaration you shouldn't run into build issue regarding declaration
Author
Owner

@ghassenbenghorbal commented on GitHub (Oct 13, 2025):

Any updates ?

<!-- gh-comment-id:3399326216 --> @ghassenbenghorbal commented on GitHub (Oct 13, 2025): Any updates ?
Author
Owner

@Pyakz commented on GitHub (Oct 16, 2025):

any update on mono repo? i am using turbo, generated from this

https://bhvr.dev/

<!-- gh-comment-id:3411360820 --> @Pyakz commented on GitHub (Oct 16, 2025): any update on mono repo? i am using turbo, generated from this https://bhvr.dev/
Author
Owner

@jweberde commented on GitHub (Oct 17, 2025):

I had a problem with pnpm turbo a package containing the basic auth config and a web application containing the actual client init. I am using better-auth-ui so i wanted to move the additional fields "typesafe" between the different plugins, and ended up having trouble during building.

root

  • packages/auth <- BetterAuthConfig as const (has peerDependency via catalog better auth version)
  • apps/web-auth <- betterAuth(...)

At the end and after trying a lot i ended up with following solution for my problem:

import type { BetterAuthOptions } from "better-auth";
import type { OrganizationOptions } from "better-auth/plugins/organization";
import { ac, roles } from "./permissions";

export const authDefaultOptions = {
  emailAndPassword: {
    enabled: true,
  },
  user: {
    changeEmail: {
      enabled: false,
    },
    deleteUser: {
      enabled: false,
    },

    additionalFields: {
      jobTitle: {
        type: "string",
        required: false,
        input: true,
      }
    },
  },
} as const satisfies BetterAuthOptions;

I added a as const and then the satisfies construct.

Why?

  • as const preserves literal types (e.g., "rbx_sess", "sliding"), readonly arrays, and nested object literal specificity.
  • satisfies BetterAuthConfig checks the shape without widening the value to BetterAuthConfig, so auth(authConfig) still gets full inference.

When you look at the generated .d.ts file containing your types having as const vs not as const results in the following:

With as const

declare const authDefaultOptions: {
    readonly emailAndPassword: {
        readonly enabled: true;
    };
    readonly user: {
        readonly changeEmail: {
            readonly enabled: false;
        };
        readonly deleteUser: {
            readonly enabled: false;
        };
        readonly additionalFields: {
            readonly jobTitle: {
                readonly type: "string";
                readonly required: false;
                readonly input: true;
            };
        };
    };
};

vs.

without as const

declare const authDefaultOptions: {
    emailAndPassword: {
        enabled: true;
    };
    user: {
        changeEmail: {
            enabled: false;
        };
        deleteUser: {
            enabled: false;
        };
        additionalFields: {
            jobTitle: {
                type: string;
                required: boolean;
                input: boolean;
            };
        };
    };
};

Hope that helps someone struggling with the same problem

<!-- gh-comment-id:3415724840 --> @jweberde commented on GitHub (Oct 17, 2025): I had a problem with pnpm turbo a package containing the basic auth config and a web application containing the actual client init. I am using better-auth-ui so i wanted to move the additional fields "typesafe" between the different plugins, and ended up having trouble during building. root - packages/auth <- BetterAuthConfig as const (has peerDependency via catalog better auth version) - apps/web-auth <- betterAuth(...) At the end and after trying a lot i ended up with following solution for my problem: ```typescript import type { BetterAuthOptions } from "better-auth"; import type { OrganizationOptions } from "better-auth/plugins/organization"; import { ac, roles } from "./permissions"; export const authDefaultOptions = { emailAndPassword: { enabled: true, }, user: { changeEmail: { enabled: false, }, deleteUser: { enabled: false, }, additionalFields: { jobTitle: { type: "string", required: false, input: true, } }, }, } as const satisfies BetterAuthOptions; ``` I added a **as const** and then the satisfies construct. Why? - as const preserves literal types (e.g., "rbx_sess", "sliding"), readonly arrays, and nested object literal specificity. - satisfies BetterAuthConfig checks the shape without widening the value to BetterAuthConfig, so auth(authConfig) still gets full inference. When you look at the generated .d.ts file containing your types having _as const_ vs _not as const_ results in the following: **With as const** ```typescript declare const authDefaultOptions: { readonly emailAndPassword: { readonly enabled: true; }; readonly user: { readonly changeEmail: { readonly enabled: false; }; readonly deleteUser: { readonly enabled: false; }; readonly additionalFields: { readonly jobTitle: { readonly type: "string"; readonly required: false; readonly input: true; }; }; }; }; ``` vs. **without as const** ```typescript declare const authDefaultOptions: { emailAndPassword: { enabled: true; }; user: { changeEmail: { enabled: false; }; deleteUser: { enabled: false; }; additionalFields: { jobTitle: { type: string; required: boolean; input: boolean; }; }; }; }; ``` Hope that helps someone struggling with the same problem
Author
Owner

@ghassenbenghorbal commented on GitHub (Oct 17, 2025):

I had a problem with pnpm turbo a package containing the basic auth config and a web application containing the actual client init. I am using better-auth-ui so i wanted to move the additional fields "typesafe" between the different plugins, and ended up having trouble during building.

root

  • packages/auth <- BetterAuthConfig as const (has peerDependency via catalog better auth version)
  • apps/web-auth <- betterAuth(...)

At the end and after trying a lot i ended up with following solution for my problem:

import type { BetterAuthOptions } from "better-auth";
import type { OrganizationOptions } from "better-auth/plugins/organization";
import { ac, roles } from "./permissions";

export const authDefaultOptions = {
emailAndPassword: {
enabled: true,
},
user: {
changeEmail: {
enabled: false,
},
deleteUser: {
enabled: false,
},

additionalFields: {
  jobTitle: {
    type: "string",
    required: false,
    input: true,
  }
},

},
} as const satisfies BetterAuthOptions;
I added a as const and then the satisfies construct.

Why?

  • as const preserves literal types (e.g., "rbx_sess", "sliding"), readonly arrays, and nested object literal specificity.
  • satisfies BetterAuthConfig checks the shape without widening the value to BetterAuthConfig, so auth(authConfig) still gets full inference.

When you look at the generated .d.ts file containing your types having as const vs not as const results in the following:

With as const

declare const authDefaultOptions: {
readonly emailAndPassword: {
readonly enabled: true;
};
readonly user: {
readonly changeEmail: {
readonly enabled: false;
};
readonly deleteUser: {
readonly enabled: false;
};
readonly additionalFields: {
readonly jobTitle: {
readonly type: "string";
readonly required: false;
readonly input: true;
};
};
};
};
vs.

without as const

declare const authDefaultOptions: {
emailAndPassword: {
enabled: true;
};
user: {
changeEmail: {
enabled: false;
};
deleteUser: {
enabled: false;
};
additionalFields: {
jobTitle: {
type: string;
required: boolean;
input: boolean;
};
};
};
};
Hope that helps someone struggling with the same problem

This doesn't work with plugins unfortunately

<!-- gh-comment-id:3417421874 --> @ghassenbenghorbal commented on GitHub (Oct 17, 2025): # > I had a problem with pnpm turbo a package containing the basic auth config and a web application containing the actual client init. I am using better-auth-ui so i wanted to move the additional fields "typesafe" between the different plugins, and ended up having trouble during building. > > root > > * packages/auth <- BetterAuthConfig as const (has peerDependency via catalog better auth version) > * apps/web-auth <- betterAuth(...) > > At the end and after trying a lot i ended up with following solution for my problem: > > import type { BetterAuthOptions } from "better-auth"; > import type { OrganizationOptions } from "better-auth/plugins/organization"; > import { ac, roles } from "./permissions"; > > export const authDefaultOptions = { > emailAndPassword: { > enabled: true, > }, > user: { > changeEmail: { > enabled: false, > }, > deleteUser: { > enabled: false, > }, > > additionalFields: { > jobTitle: { > type: "string", > required: false, > input: true, > } > }, > }, > } as const satisfies BetterAuthOptions; > I added a **as const** and then the satisfies construct. > > Why? > > * as const preserves literal types (e.g., "rbx_sess", "sliding"), readonly arrays, and nested object literal specificity. > * satisfies BetterAuthConfig checks the shape without widening the value to BetterAuthConfig, so auth(authConfig) still gets full inference. > > When you look at the generated .d.ts file containing your types having _as const_ vs _not as const_ results in the following: > > **With as const** > > declare const authDefaultOptions: { > readonly emailAndPassword: { > readonly enabled: true; > }; > readonly user: { > readonly changeEmail: { > readonly enabled: false; > }; > readonly deleteUser: { > readonly enabled: false; > }; > readonly additionalFields: { > readonly jobTitle: { > readonly type: "string"; > readonly required: false; > readonly input: true; > }; > }; > }; > }; > vs. > > **without as const** > > declare const authDefaultOptions: { > emailAndPassword: { > enabled: true; > }; > user: { > changeEmail: { > enabled: false; > }; > deleteUser: { > enabled: false; > }; > additionalFields: { > jobTitle: { > type: string; > required: boolean; > input: boolean; > }; > }; > }; > }; > Hope that helps someone struggling with the same problem This doesn't work with plugins unfortunately
Author
Owner

@ghassenbenghorbal commented on GitHub (Oct 22, 2025):

Any updates ? I have to wrap the better auth api with "any" everytime I wanna use it to avoid this issue. It would be great if this gets fixed asap

<!-- gh-comment-id:3431976326 --> @ghassenbenghorbal commented on GitHub (Oct 22, 2025): Any updates ? I have to wrap the better auth api with "any" everytime I wanna use it to avoid this issue. It would be great if this gets fixed asap
Author
Owner

@Bekacru commented on GitHub (Oct 26, 2025):

@ghassenbenghorbal can you remove the type casting
//as ReturnType<
typeof betterAuth

;

and send me the type error

<!-- gh-comment-id:3447902931 --> @Bekacru commented on GitHub (Oct 26, 2025): @ghassenbenghorbal can you remove the type casting //as ReturnType< typeof betterAuth<typeof authConfig> >; and send me the type error
Author
Owner

@ghassenbenghorbal commented on GitHub (Oct 26, 2025):

@Bekacru src/auth.ts(89,14): error TS2742: The inferred type of 'auth' cannot be named without a reference to '.bun/@better-auth+core@1.3.31+291433d3a1a8a313/node_modules/@better-auth/core/db/adapter'. This is likely not portable. A type annotation is necessary.

<!-- gh-comment-id:3447971135 --> @ghassenbenghorbal commented on GitHub (Oct 26, 2025): @Bekacru `src/auth.ts(89,14): error TS2742: The inferred type of 'auth' cannot be named without a reference to '.bun/@better-auth+core@1.3.31+291433d3a1a8a313/node_modules/@better-auth/core/db/adapter'. This is likely not portable. A type annotation is necessary.`
Author
Owner

@himself65 commented on GitHub (Oct 28, 2025):

I can confirm this is fixed in the 1.4 beta version. It's not portable back to 1.3, since we did a huge type refactor over 1k diff for the organization plugin to fix this.

Refs: a58f11a28f, eee94de01a ,4b5f637bf0

<!-- gh-comment-id:3458544564 --> @himself65 commented on GitHub (Oct 28, 2025): I can confirm this is fixed in the 1.4 beta version. It's not portable back to 1.3, since we did a huge type refactor over 1k diff for the organization plugin to fix this. Refs: https://github.com/better-auth/better-auth/commit/a58f11a28f2fa1576065920155d88859d42e3bab, https://github.com/better-auth/better-auth/commit/eee94de01a55d5ec125cf36b26cda4c461cc9d97 ,https://github.com/better-auth/better-auth/commit/4b5f637bf075d01413e592d57fa4080ac36cdee5
Author
Owner

@j-fdion commented on GitHub (Oct 29, 2025):

I did have some problems like @ghassenbenghorbal with vscode intellisense, but deleting node_modules/reinstalling packages/restarting the editor made it work.

<!-- gh-comment-id:3462835735 --> @j-fdion commented on GitHub (Oct 29, 2025): I did have some problems like @ghassenbenghorbal with vscode intellisense, but deleting node_modules/reinstalling packages/restarting the editor made it work.
Author
Owner

@ping-maxwell commented on GitHub (Jan 9, 2026):

Hello all, given how broad this issue is, as well as it slowly going stale & it being fixed in 1.4. I'm going to close this issue.
If this is still an issue or if you're new to this thread please open a new issue instead and make it very specific to your case to reduce potentially unrelated issues being brought into the issue threads. Having more specific issues will help us with solving them!

<!-- gh-comment-id:3726954413 --> @ping-maxwell commented on GitHub (Jan 9, 2026): Hello all, given how broad this issue is, as well as it slowly going stale & it being fixed in 1.4. I'm going to close this issue. If this is still an issue or if you're new to this thread please open a new issue instead and make it very specific to your case to reduce potentially unrelated issues being brought into the issue threads. Having more specific issues will help us with solving 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#27492