better auth dash infra plugin import problem #2995

Closed
opened 2026-03-13 10:33:22 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @Abdelkader-gnichi on GitHub (Mar 5, 2026).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

when installing better auth infra library and import the dash plugin in /lib/auth.ts and configure it in the better auth plugins section using the my better auth infra api key, get an error when i run the project at the first time.
the error it seams to be related to how better auth infra library handles imports.

here is the error:

node:internal/modules/cjs/loader:686
      throw e;
      ^

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /home/piva/Desktop/USP_CONTROLLER/auth-service/node_modules/@better-auth/infra/package.json
    at exportsNotFound (node:internal/modules/esm/resolve:313:10)
    at packageExportsResolve (node:internal/modules/esm/resolve:604:13)
    at resolveExports (node:internal/modules/cjs/loader:679:36)
    at Module._findPath (node:internal/modules/cjs/loader:746:31)
    at Module._resolveFilename (node:internal/modules/cjs/loader:1406:27)
    at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)
    at Module._load (node:internal/modules/cjs/loader:1227:37)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:245:24) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

Node.js v24.11.1

i have switched to bun instead of nodejs but i still have the same problem, i know it is not a js runtime related problem but i was hoping that there is a stable version in other js runtime and package managers

Current vs. Expected behavior

when installing better auth infra library and import the dash plugin in /lib/auth.ts and configure it in the better auth plugins section using the my better auth infra api key, get an error when i run the project at the first time.
the error it seams to be related to how better auth infra library handles imports.

here is the error:

node:internal/modules/cjs/loader:686
      throw e;
      ^

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /home/piva/Desktop/USP_CONTROLLER/auth-service/node_modules/@better-auth/infra/package.json
    at exportsNotFound (node:internal/modules/esm/resolve:313:10)
    at packageExportsResolve (node:internal/modules/esm/resolve:604:13)
    at resolveExports (node:internal/modules/cjs/loader:679:36)
    at Module._findPath (node:internal/modules/cjs/loader:746:31)
    at Module._resolveFilename (node:internal/modules/cjs/loader:1406:27)
    at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)
    at Module._load (node:internal/modules/cjs/loader:1227:37)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:245:24) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

Node.js v24.11.1

i have switched to bun instead of nodejs but i still have the same problem, i know it is not a js runtime related problem but i was hoping that there is a stable version in other js runtime and package managers

the expected behavior is my nestjs better auth service runs correctly and let me use better auth dash() plugin without any issues since i have configured it correctly

What version of Better Auth are you using?

1.5.3

System info

{
"system": {
"platform": "linux",
"arch": "x64",
"version": "#101~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Feb 11 13:19:54 UTC ",
"release": "6.8.0-101-generic",
"cpuCount": 8,
"cpuModel": "11th Gen Intel(R) Core(TM) i7-11370H @ 3.30GHz",
"totalMemory": "23.17 GB",
"freeMemory": "3.79 GB"
},
"node": {
"version": "v24.11.1",
"env": "development"
},
"packageManager": {
"name": "npm",
"version": "11.6.2"
},
"frameworks": null,
"databases": [
{
"name": "pg",
"version": "^8.20.0"
},
{
"name": "drizzle",
"version": "^0.45.1"
}
],
"betterAuth": {
"version": "^1.5.3",
"config": {
"hooks": {},
"emailAndPassword": {
"enabled": true
},
"session": {
"expiresIn": 604800,
"updateAge": 86400
},
"plugins": [
{
"name": "admin",
"config": {
"id": "admin",
"hooks": {
"after": [
{}
]
},
"endpoints": {},
"$ERROR_CODES": {
"FAILED_TO_CREATE_USER": {
"code": "FAILED_TO_CREATE_USER",
"message": "Failed to create user"
},
"USER_ALREADY_EXISTS": {
"code": "USER_ALREADY_EXISTS",
"message": "User already exists."
},
"USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": {
"code": "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL",
"message": "User already exists. Use another email."
},
"YOU_CANNOT_BAN_YOURSELF": {
"code": "YOU_CANNOT_BAN_YOURSELF",
"message": "You cannot ban yourself"
},
"YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": {
"code": "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE",
"message": "You are not allowed to change users role"
},
"YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS",
"message": "You are not allowed to create users"
},
"YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS",
"message": "You are not allowed to list users"
},
"YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS",
"message": "You are not allowed to list users sessions"
},
"YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS",
"message": "You are not allowed to ban users"
},
"YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS",
"message": "You are not allowed to impersonate users"
},
"YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS",
"message": "You are not allowed to revoke users sessions"
},
"YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS",
"message": "You are not allowed to delete users"
},
"YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": {
"code": "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD",
"message": "You are not allowed to set users password"
},
"BANNED_USER": {
"code": "BANNED_USER",
"message": "You have been banned from this application"
},
"YOU_ARE_NOT_ALLOWED_TO_GET_USER": {
"code": "YOU_ARE_NOT_ALLOWED_TO_GET_USER",
"message": "You are not allowed to get user"
},
"NO_DATA_TO_UPDATE": {
"code": "NO_DATA_TO_UPDATE",
"message": "No data to update"
},
"YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": {
"code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS",
"message": "You are not allowed to update users"
},
"YOU_CANNOT_REMOVE_YOURSELF": {
"code": "YOU_CANNOT_REMOVE_YOURSELF",
"message": "You cannot remove yourself"
},
"YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": {
"code": "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE",
"message": "You are not allowed to set a non-existent role value"
},
"YOU_CANNOT_IMPERSONATE_ADMINS": {
"code": "YOU_CANNOT_IMPERSONATE_ADMINS",
"message": "You cannot impersonate admins"
},
"INVALID_ROLE_TYPE": {
"code": "INVALID_ROLE_TYPE",
"message": "Invalid role type"
}
},
"schema": {
"user": {
"fields": {
"role": {
"type": "string",
"required": false,
"input": false
},
"banned": {
"type": "boolean",
"defaultValue": false,
"required": false,
"input": false
},
"banReason": {
"type": "string",
"required": false,
"input": false
},
"banExpires": {
"type": "date",
"required": false,
"input": false
}
}
},
"session": {
"fields": {
"impersonatedBy": {
"type": "string",
"required": false
}
}
}
}
}
},
{
"name": "organization",
"config": {
"id": "organization",
"endpoints": {},
"schema": {
"organization": {
"fields": {
"name": {
"type": "string",
"required": true,
"sortable": true
},
"slug": {
"type": "string",
"required": true,
"unique": true,
"sortable": true,
"index": true
},
"logo": {
"type": "string",
"required": false
},
"createdAt": {
"type": "date",
"required": true
},
"metadata": {
"type": "string",
"required": false
},
"username": {
"type": "string",
"defaultValue": ""
},
"password": {
"type": "string",
"defaultValue": ""
}
}
},
"member": {
"fields": {
"organizationId": {
"type": "string",
"required": true,
"references": {
"model": "organization",
"field": "id"
},
"index": true
},
"userId": {
"type": "string",
"required": true,
"references": {
"model": "user",
"field": "id"
},
"index": true
},
"role": {
"type": "string",
"required": true,
"sortable": true,
"defaultValue": "member"
},
"createdAt": {
"type": "date",
"required": true
}
}
},
"invitation": {
"fields": {
"organizationId": {
"type": "string",
"required": true,
"references": {
"model": "organization",
"field": "id"
},
"index": true
},
"email": {
"type": "string",
"required": true,
"sortable": true,
"index": true
},
"role": {
"type": "string",
"required": false,
"sortable": true
},
"status": {
"type": "string",
"required": true,
"sortable": true,
"defaultValue": "pending"
},
"expiresAt": {
"type": "date",
"required": true
},
"createdAt": {
"type": "date",
"required": true
},
"inviterId": {
"type": "string",
"references": {
"model": "user",
"field": "id"
},
"required": true
}
}
},
"session": {
"fields": {
"activeOrganizationId": {
"type": "string",
"required": false
}
}
}
},
"$Infer": {
"Organization": {},
"Invitation": {},
"Member": {},
"Team": {},
"TeamMember": {},
"ActiveOrganization": {}
},
"$ERROR_CODES": {
"YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION": {
"code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION",
"message": "You are not allowed to create a new organization"
},
"YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS": {
"code": "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS",
"message": "You have reached the maximum number of organizations"
},
"ORGANIZATION_ALREADY_EXISTS": {
"code": "ORGANIZATION_ALREADY_EXISTS",
"message": "Organization already exists"
},
"ORGANIZATION_SLUG_ALREADY_TAKEN": {
"code": "ORGANIZATION_SLUG_ALREADY_TAKEN",
"message": "Organization slug already taken"
},
"ORGANIZATION_NOT_FOUND": {
"code": "ORGANIZATION_NOT_FOUND",
"message": "Organization not found"
},
"USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION": {
"code": "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION",
"message": "User is not a member of the organization"
},
"YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION": {
"code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION",
"message": "You are not allowed to update this organization"
},
"YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION": {
"code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION",
"message": "You are not allowed to delete this organization"
},
"NO_ACTIVE_ORGANIZATION": {
"code": "NO_ACTIVE_ORGANIZATION",
"message": "No active organization"
},
"USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION": {
"code": "USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION",
"message": "User is already a member of this organization"
},
"MEMBER_NOT_FOUND": {
"code": "MEMBER_NOT_FOUND",
"message": "Member not found"
},
"ROLE_NOT_FOUND": {
"code": "ROLE_NOT_FOUND",
"message": "Role not found"
},
"YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM": {
"code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM",
"message": "You are not allowed to create a new team"
},
"TEAM_ALREADY_EXISTS": {
"code": "TEAM_ALREADY_EXISTS",
"message": "Team already exists"
},
"TEAM_NOT_FOUND": {
"code": "TEAM_NOT_FOUND",
"message": "Team not found"
},
"YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER": {
"code": "YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER",
"message": "You cannot leave the organization as the only owner"
},
"YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER": {
"code": "YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER",
"message": "You cannot leave the organization without an owner"
},
"YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER": {
"code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER",
"message": "You are not allowed to delete this member"
},
"YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION": {
"code": "YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION",
"message": "You are not allowed to invite users to this organization"
},
"USER_IS_ALREADY_INVITED_TO_THIS_ORGA...

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

Backend, Package

Auth config (if applicable)

import { config } from 'dotenv';
import { resolve } from 'path';
import { betterAuth } from 'better-auth';
import { createAuthMiddleware, APIError } from 'better-auth/api';
import { drizzleAdapter } from '@better-auth/drizzle-adapter';
import { admin, organization, jwt, bearer, openAPI } from 'better-auth/plugins';
import { db } from './database';
import * as nodemailer from 'nodemailer';
import { ac, owner, admin as roleAdmin, member } from './permissions';
import { count, eq, and, inArray } from 'drizzle-orm';
import { user as userTable, member as memberTable } from './schema';
import { dash } from "@better-auth/infra";
const rootEnvPath = resolve(process.cwd(), '../.env');
config({ path: rootEnvPath });

console.log('Loading auth config...');
console.log('Root ENV path:', rootEnvPath);
console.log('AUTH_DATABASE_URL defined:', !!process.env.AUTH_DATABASE_URL);
console.log('JWT access expiry:', process.env.AUTH_JWT_ACCESS_EXPIRATION);
console.log(
  'JWT refresh (session) expiry:',
  process.env.AUTH_JWT_REFRESH_EXPIRATION,
);

export async function sendEmail({
  to,
  subject,
  text,
  html,
}: {
  to: string;
  subject: string;
  text?: string;
  html?: string;
}) {
  const transporter = nodemailer.createTransport({
    host: process.env.SMTP_HOST,
    port: Number(process.env.SMTP_PORT),
    secure: false,
    auth: {
      user: process.env.SMTP_USER,
      pass: process.env.SMTP_PASS,
    },
  });

  await transporter.sendMail({
    from: `"My App" <${process.env.SMTP_FROM}>`,
    to,
    subject,
    text,
    html,
  });
}

function parseDuration(value: string, defaultValue: number): number {
  if (!value) return defaultValue;
  const match = value.match(/^(\d+)([smhd])$/);
  if (!match) return defaultValue;

  const amount = Number(match[1]);
  const unit = match[2];

  switch (unit) {
    case 's':
      return amount;
    case 'm':
      return amount * 60;
    case 'h':
      return amount * 60 * 60;
    case 'd':
      return amount * 24 * 60 * 60;
    default:
      return defaultValue;
  }
}

const baseURL = process.env.BETTER_AUTH_URL || 'http://localhost:3002';

export const auth = betterAuth({
  hooks: {
    before: createAuthMiddleware(async (ctx) => {
      const normalizedPath = ctx.path.startsWith('/')
        ? ctx.path
        : `/${ctx.path}`;

      if (normalizedPath.endsWith('/sign-up/email')) {
        const adminEmail = process.env.ADMIN_EMAIL || 'superadmin@example.com';
        const body = (ctx.body || {}) as { email?: string };

        if (body.email === adminEmail) {
          const [result] = await db.select({ value: count() }).from(userTable);
          const userCount = Number(result.value);
          if (userCount === 0) return;
        }

        if (body.email !== adminEmail) {
          throw new APIError('FORBIDDEN', {
            message:
              'Public sign-up is strictly disabled. Only the system admin can be registered through this endpoint.',
          });
        }

        const session = await ctx.context.api.getSession({
          headers: ctx.headers,
        });

        if (
          !session ||
          (session.user.role !== 'admin' && session.user.role !== 'owner')
        ) {
          throw new APIError('FORBIDDEN', {
            message:
              'Access denied: Must be authenticated as an administrator to create users.',
          });
        }
      }
    }),
  },
  emailAndPassword: {
    enabled: true,
    // requireEmailVerification: true,
  },

  database: drizzleAdapter(db, { provider: 'pg' }),

  session: {
    expiresIn: parseDuration(
      process.env.AUTH_JWT_REFRESH_EXPIRATION || '',
      7 * 24 * 60 * 60,
    ),
    updateAge: 24 * 60 * 60,
  },
  // emailVerification: {
  //   sendOnSignUp: true,
  //   autoSignInAfterVerification: true,
  //   sendVerificationEmail: async (
  //     params: {
  //       user: {
  //         id: string;
  //         email: string;
  //         name?: string | null;
  //       };
  //       url: string;
  //       token: string;
  //     },
  //     request: Request,
  //   ) => {
  //     const { user, url } = params;
  //
  //     await sendEmail({
  //       to: user.email,
  //       subject: 'Verify your email address',
  //       text: `Click the link to verify your email: ${url}`,
  //       html: `<p>Click the link to verify your email: <a href="${url}">${url}</a></p>`,
  //     });
  //   },
  // },
  plugins: [
    admin(),
    organization({
      ac,
      roles: { owner, admin: roleAdmin, member },
      allowUserToCreateOrganization: async (user) => {
        if (user.role === 'admin' || user.role === 'owner') return true;
        const [membership] = await db
          .select()
          .from(memberTable)
          .where(
            and(
              eq(memberTable.userId, user.id),
              inArray(memberTable.role, ['owner', 'admin']),
            ),
          )
          .limit(1);

        return !!membership;
      },
      schema: {
        organization: {
          additionalFields: {
            username: { type: 'string', defaultValue: '' },
            password: { type: 'string', defaultValue: '' },
          },
        },
      },
    }),
    jwt({
      jwt: {
        expirationTime: process.env.AUTH_JWT_ACCESS_EXPIRATION || '15m',
        definePayload: (session) => {
          return {
            ...session.user,
            activeOrganizationId: session.session
              .activeOrganizationId as string,
            sessionId: session.session.id,
          };
        },
      },
    }),
    bearer(),
    openAPI(),
    dash({
       apiKey: process.env.BETTER_AUTH_API_KEY,
    }),
  ],
  secret: process.env.BETTER_AUTH_SECRET,
  baseURL,
  trustedOrigins:
    process.env.CORS_ORIGINS?.split(',').map((o) => o.trim()) || [],
});

Additional context

No response

Originally created by @Abdelkader-gnichi on GitHub (Mar 5, 2026). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce when installing better auth infra library and import the dash plugin in `/lib/auth.ts` and configure it in the better auth plugins section using the my better auth infra api key, get an error when i run the project at the first time. the error it seams to be related to how better auth infra library handles imports. here is the error: ``` node:internal/modules/cjs/loader:686       throw e;       ^ Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /home/piva/Desktop/USP_CONTROLLER/auth-service/node_modules/@better-auth/infra/package.json     at exportsNotFound (node:internal/modules/esm/resolve:313:10)     at packageExportsResolve (node:internal/modules/esm/resolve:604:13)     at resolveExports (node:internal/modules/cjs/loader:679:36)     at Module._findPath (node:internal/modules/cjs/loader:746:31)     at Module._resolveFilename (node:internal/modules/cjs/loader:1406:27)     at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)     at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)     at Module._load (node:internal/modules/cjs/loader:1227:37)     at TracingChannel.traceSync (node:diagnostics_channel:328:14)     at wrapModuleLoad (node:internal/modules/cjs/loader:245:24) {   code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' } Node.js v24.11.1 ``` i have switched to bun instead of nodejs but i still have the same problem, i know it is not a js runtime related problem but i was hoping that there is a stable version in other js runtime and package managers ### Current vs. Expected behavior when installing better auth infra library and import the dash plugin in `/lib/auth.ts` and configure it in the better auth plugins section using the my better auth infra api key, get an error when i run the project at the first time. the error it seams to be related to how better auth infra library handles imports. here is the error: ``` node:internal/modules/cjs/loader:686       throw e;       ^ Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /home/piva/Desktop/USP_CONTROLLER/auth-service/node_modules/@better-auth/infra/package.json     at exportsNotFound (node:internal/modules/esm/resolve:313:10)     at packageExportsResolve (node:internal/modules/esm/resolve:604:13)     at resolveExports (node:internal/modules/cjs/loader:679:36)     at Module._findPath (node:internal/modules/cjs/loader:746:31)     at Module._resolveFilename (node:internal/modules/cjs/loader:1406:27)     at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)     at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)     at Module._load (node:internal/modules/cjs/loader:1227:37)     at TracingChannel.traceSync (node:diagnostics_channel:328:14)     at wrapModuleLoad (node:internal/modules/cjs/loader:245:24) {   code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' } Node.js v24.11.1 ``` i have switched to bun instead of nodejs but i still have the same problem, i know it is not a js runtime related problem but i was hoping that there is a stable version in other js runtime and package managers the expected behavior is my nestjs better auth service runs correctly and let me use better auth dash() plugin without any issues since i have configured it correctly ### What version of Better Auth are you using? 1.5.3 ### System info ```bash { "system": { "platform": "linux", "arch": "x64", "version": "#101~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Feb 11 13:19:54 UTC ", "release": "6.8.0-101-generic", "cpuCount": 8, "cpuModel": "11th Gen Intel(R) Core(TM) i7-11370H @ 3.30GHz", "totalMemory": "23.17 GB", "freeMemory": "3.79 GB" }, "node": { "version": "v24.11.1", "env": "development" }, "packageManager": { "name": "npm", "version": "11.6.2" }, "frameworks": null, "databases": [ { "name": "pg", "version": "^8.20.0" }, { "name": "drizzle", "version": "^0.45.1" } ], "betterAuth": { "version": "^1.5.3", "config": { "hooks": {}, "emailAndPassword": { "enabled": true }, "session": { "expiresIn": 604800, "updateAge": 86400 }, "plugins": [ { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": { "code": "FAILED_TO_CREATE_USER", "message": "Failed to create user" }, "USER_ALREADY_EXISTS": { "code": "USER_ALREADY_EXISTS", "message": "User already exists." }, "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": { "code": "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL", "message": "User already exists. Use another email." }, "YOU_CANNOT_BAN_YOURSELF": { "code": "YOU_CANNOT_BAN_YOURSELF", "message": "You cannot ban yourself" }, "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE", "message": "You are not allowed to change users role" }, "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": { "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS", "message": "You are not allowed to create users" }, "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": { "code": "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS", "message": "You are not allowed to list users" }, "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": { "code": "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS", "message": "You are not allowed to list users sessions" }, "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": { "code": "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS", "message": "You are not allowed to ban users" }, "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": { "code": "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS", "message": "You are not allowed to impersonate users" }, "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": { "code": "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS", "message": "You are not allowed to revoke users sessions" }, "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": { "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS", "message": "You are not allowed to delete users" }, "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": { "code": "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD", "message": "You are not allowed to set users password" }, "BANNED_USER": { "code": "BANNED_USER", "message": "You have been banned from this application" }, "YOU_ARE_NOT_ALLOWED_TO_GET_USER": { "code": "YOU_ARE_NOT_ALLOWED_TO_GET_USER", "message": "You are not allowed to get user" }, "NO_DATA_TO_UPDATE": { "code": "NO_DATA_TO_UPDATE", "message": "No data to update" }, "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": { "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS", "message": "You are not allowed to update users" }, "YOU_CANNOT_REMOVE_YOURSELF": { "code": "YOU_CANNOT_REMOVE_YOURSELF", "message": "You cannot remove yourself" }, "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": { "code": "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE", "message": "You are not allowed to set a non-existent role value" }, "YOU_CANNOT_IMPERSONATE_ADMINS": { "code": "YOU_CANNOT_IMPERSONATE_ADMINS", "message": "You cannot impersonate admins" }, "INVALID_ROLE_TYPE": { "code": "INVALID_ROLE_TYPE", "message": "Invalid role type" } }, "schema": { "user": { "fields": { "role": { "type": "string", "required": false, "input": false }, "banned": { "type": "boolean", "defaultValue": false, "required": false, "input": false }, "banReason": { "type": "string", "required": false, "input": false }, "banExpires": { "type": "date", "required": false, "input": false } } }, "session": { "fields": { "impersonatedBy": { "type": "string", "required": false } } } } } }, { "name": "organization", "config": { "id": "organization", "endpoints": {}, "schema": { "organization": { "fields": { "name": { "type": "string", "required": true, "sortable": true }, "slug": { "type": "string", "required": true, "unique": true, "sortable": true, "index": true }, "logo": { "type": "string", "required": false }, "createdAt": { "type": "date", "required": true }, "metadata": { "type": "string", "required": false }, "username": { "type": "string", "defaultValue": "" }, "password": { "type": "string", "defaultValue": "" } } }, "member": { "fields": { "organizationId": { "type": "string", "required": true, "references": { "model": "organization", "field": "id" }, "index": true }, "userId": { "type": "string", "required": true, "references": { "model": "user", "field": "id" }, "index": true }, "role": { "type": "string", "required": true, "sortable": true, "defaultValue": "member" }, "createdAt": { "type": "date", "required": true } } }, "invitation": { "fields": { "organizationId": { "type": "string", "required": true, "references": { "model": "organization", "field": "id" }, "index": true }, "email": { "type": "string", "required": true, "sortable": true, "index": true }, "role": { "type": "string", "required": false, "sortable": true }, "status": { "type": "string", "required": true, "sortable": true, "defaultValue": "pending" }, "expiresAt": { "type": "date", "required": true }, "createdAt": { "type": "date", "required": true }, "inviterId": { "type": "string", "references": { "model": "user", "field": "id" }, "required": true } } }, "session": { "fields": { "activeOrganizationId": { "type": "string", "required": false } } } }, "$Infer": { "Organization": {}, "Invitation": {}, "Member": {}, "Team": {}, "TeamMember": {}, "ActiveOrganization": {} }, "$ERROR_CODES": { "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION", "message": "You are not allowed to create a new organization" }, "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS": { "code": "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS", "message": "You have reached the maximum number of organizations" }, "ORGANIZATION_ALREADY_EXISTS": { "code": "ORGANIZATION_ALREADY_EXISTS", "message": "Organization already exists" }, "ORGANIZATION_SLUG_ALREADY_TAKEN": { "code": "ORGANIZATION_SLUG_ALREADY_TAKEN", "message": "Organization slug already taken" }, "ORGANIZATION_NOT_FOUND": { "code": "ORGANIZATION_NOT_FOUND", "message": "Organization not found" }, "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION": { "code": "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION", "message": "User is not a member of the organization" }, "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION", "message": "You are not allowed to update this organization" }, "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION", "message": "You are not allowed to delete this organization" }, "NO_ACTIVE_ORGANIZATION": { "code": "NO_ACTIVE_ORGANIZATION", "message": "No active organization" }, "USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION": { "code": "USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION", "message": "User is already a member of this organization" }, "MEMBER_NOT_FOUND": { "code": "MEMBER_NOT_FOUND", "message": "Member not found" }, "ROLE_NOT_FOUND": { "code": "ROLE_NOT_FOUND", "message": "Role not found" }, "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM": { "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM", "message": "You are not allowed to create a new team" }, "TEAM_ALREADY_EXISTS": { "code": "TEAM_ALREADY_EXISTS", "message": "Team already exists" }, "TEAM_NOT_FOUND": { "code": "TEAM_NOT_FOUND", "message": "Team not found" }, "YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER": { "code": "YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER", "message": "You cannot leave the organization as the only owner" }, "YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER": { "code": "YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER", "message": "You cannot leave the organization without an owner" }, "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER": { "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER", "message": "You are not allowed to delete this member" }, "YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION", "message": "You are not allowed to invite users to this organization" }, "USER_IS_ALREADY_INVITED_TO_THIS_ORGA... ``` ### Which area(s) are affected? (Select all that apply) Backend, Package ### Auth config (if applicable) ```typescript import { config } from 'dotenv'; import { resolve } from 'path'; import { betterAuth } from 'better-auth'; import { createAuthMiddleware, APIError } from 'better-auth/api'; import { drizzleAdapter } from '@better-auth/drizzle-adapter'; import { admin, organization, jwt, bearer, openAPI } from 'better-auth/plugins'; import { db } from './database'; import * as nodemailer from 'nodemailer'; import { ac, owner, admin as roleAdmin, member } from './permissions'; import { count, eq, and, inArray } from 'drizzle-orm'; import { user as userTable, member as memberTable } from './schema'; import { dash } from "@better-auth/infra"; const rootEnvPath = resolve(process.cwd(), '../.env'); config({ path: rootEnvPath }); console.log('Loading auth config...'); console.log('Root ENV path:', rootEnvPath); console.log('AUTH_DATABASE_URL defined:', !!process.env.AUTH_DATABASE_URL); console.log('JWT access expiry:', process.env.AUTH_JWT_ACCESS_EXPIRATION); console.log( 'JWT refresh (session) expiry:', process.env.AUTH_JWT_REFRESH_EXPIRATION, ); export async function sendEmail({ to, subject, text, html, }: { to: string; subject: string; text?: string; html?: string; }) { const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: Number(process.env.SMTP_PORT), secure: false, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }); await transporter.sendMail({ from: `"My App" <${process.env.SMTP_FROM}>`, to, subject, text, html, }); } function parseDuration(value: string, defaultValue: number): number { if (!value) return defaultValue; const match = value.match(/^(\d+)([smhd])$/); if (!match) return defaultValue; const amount = Number(match[1]); const unit = match[2]; switch (unit) { case 's': return amount; case 'm': return amount * 60; case 'h': return amount * 60 * 60; case 'd': return amount * 24 * 60 * 60; default: return defaultValue; } } const baseURL = process.env.BETTER_AUTH_URL || 'http://localhost:3002'; export const auth = betterAuth({ hooks: { before: createAuthMiddleware(async (ctx) => { const normalizedPath = ctx.path.startsWith('/') ? ctx.path : `/${ctx.path}`; if (normalizedPath.endsWith('/sign-up/email')) { const adminEmail = process.env.ADMIN_EMAIL || 'superadmin@example.com'; const body = (ctx.body || {}) as { email?: string }; if (body.email === adminEmail) { const [result] = await db.select({ value: count() }).from(userTable); const userCount = Number(result.value); if (userCount === 0) return; } if (body.email !== adminEmail) { throw new APIError('FORBIDDEN', { message: 'Public sign-up is strictly disabled. Only the system admin can be registered through this endpoint.', }); } const session = await ctx.context.api.getSession({ headers: ctx.headers, }); if ( !session || (session.user.role !== 'admin' && session.user.role !== 'owner') ) { throw new APIError('FORBIDDEN', { message: 'Access denied: Must be authenticated as an administrator to create users.', }); } } }), }, emailAndPassword: { enabled: true, // requireEmailVerification: true, }, database: drizzleAdapter(db, { provider: 'pg' }), session: { expiresIn: parseDuration( process.env.AUTH_JWT_REFRESH_EXPIRATION || '', 7 * 24 * 60 * 60, ), updateAge: 24 * 60 * 60, }, // emailVerification: { // sendOnSignUp: true, // autoSignInAfterVerification: true, // sendVerificationEmail: async ( // params: { // user: { // id: string; // email: string; // name?: string | null; // }; // url: string; // token: string; // }, // request: Request, // ) => { // const { user, url } = params; // // await sendEmail({ // to: user.email, // subject: 'Verify your email address', // text: `Click the link to verify your email: ${url}`, // html: `<p>Click the link to verify your email: <a href="${url}">${url}</a></p>`, // }); // }, // }, plugins: [ admin(), organization({ ac, roles: { owner, admin: roleAdmin, member }, allowUserToCreateOrganization: async (user) => { if (user.role === 'admin' || user.role === 'owner') return true; const [membership] = await db .select() .from(memberTable) .where( and( eq(memberTable.userId, user.id), inArray(memberTable.role, ['owner', 'admin']), ), ) .limit(1); return !!membership; }, schema: { organization: { additionalFields: { username: { type: 'string', defaultValue: '' }, password: { type: 'string', defaultValue: '' }, }, }, }, }), jwt({ jwt: { expirationTime: process.env.AUTH_JWT_ACCESS_EXPIRATION || '15m', definePayload: (session) => { return { ...session.user, activeOrganizationId: session.session .activeOrganizationId as string, sessionId: session.session.id, }; }, }, }), bearer(), openAPI(), dash({ apiKey: process.env.BETTER_AUTH_API_KEY, }), ], secret: process.env.BETTER_AUTH_SECRET, baseURL, trustedOrigins: process.env.CORS_ORIGINS?.split(',').map((o) => o.trim()) || [], }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 10:33:22 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Mar 5, 2026):

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

This is a known bug with @better-auth/infra. The package's package.json uses the "import" condition in its exports field instead of "default", which causes the CJS loader to fail in CommonJS environments like NestJS.

Your import syntax is correct—the issue is in the package itself, not your code.

Unfortunately, there's no consumer-side workaround. The fix requires updating the @better-auth/infra package's exports configuration. A similar fix was applied to the SSO package, so the maintainers are aware of this pattern.

Options:

  1. Track Issue #8332 for updates
  2. If feasible, try using a pure ESM setup instead of CommonJS in your project
  3. Watch for a new version of @better-auth/infra beyond v0.1.8 that may include the fix

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

@dosubot[bot] commented on GitHub (Mar 5, 2026): <!-- Greeting --> Hi @Abdelkader-gnichi! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a [known bug with @better-auth/infra](https://github.com/better-auth/better-auth/issues/8332). The package's `package.json` uses the `"import"` condition in its exports field instead of `"default"`, which causes the CJS loader to fail in CommonJS environments like NestJS. Your import syntax is correct—the issue is in the package itself, not your code. **Unfortunately, there's no consumer-side workaround.** The fix requires updating the `@better-auth/infra` package's exports configuration. A [similar fix was applied to the SSO package](https://github.com/better-auth/better-auth/pull/8041), so the maintainers are aware of this pattern. **Options:** 1. Track [Issue #8332](https://github.com/better-auth/better-auth/issues/8332) for updates 2. If feasible, try using a pure ESM setup instead of CommonJS in your project 3. Watch for a new version of `@better-auth/infra` beyond v0.1.8 that may include the fix <!-- 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/a36ce4c2-f3b1-4d45-926e-531a03ed7471?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/a36ce4c2-f3b1-4d45-926e-531a03ed7471?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/a36ce4c2-f3b1-4d45-926e-531a03ed7471?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/a36ce4c2-f3b1-4d45-926e-531a03ed7471?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/a36ce4c2-f3b1-4d45-926e-531a03ed7471?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/a36ce4c2-f3b1-4d45-926e-531a03ed7471?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/a36ce4c2-f3b1-4d45-926e-531a03ed7471?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=This%20is%20a%20%5Bknown%20bug%20with%20%40better-auth/infra%5D%28https%3A//github.com/better-auth/better-auth/issues/8332%29.%20The%20package%27s%20%60package.json%60%20uses%20the%20%60%22import%22%60%20condition%20in%20its%20exports%20field%20instead%20of%20%60%22default%22%60%2C%20which%20causes%20the%20CJS%20loader%20to%20fail%20in%20CommonJS%20environments%20like%20NestJS.%0A%0AYour%20import%20syntax%20is%20correct%E2%80%94the%20issue%20is%20in%20the%20package%20itself%2C%20not%20your%20code.%0A%0A%2A%2AUnfortunately%2C%20there%27s%20no%20consumer-side%20workaround.%2A%2A%20The%20fix%20requires%20updating%20the%20%60%40better-auth/infra%60%20package%27s%20exports%20configuration.%20A%20%5Bsimilar%20fix%20was%20applied%20to%20the%20SSO%20package%5D%28https%3A//github.com/better-auth/better-auth/pull/8041%29%2C%20so%20the%20maintainers%20are%20aware%20of%20this%20pattern.%0A%0A%2A%2AOptions%3A%2A%2A%0A1.%20Track%20%5BIssue%20%238332%5D%28https%3A//github.com/better-auth/better-auth/issues/8332%29%20for%20updates%0A2.%20If%20feasible%2C%20try%20using%20a%20pure%20ESM%20setup%20instead%20of%20CommonJS%20in%20your%20project%0A3.%20Watch%20for%20a%20new%20version%20of%20%60%40better-auth/infra%60%20beyond%20v0.1.8%20that%20may%20include%20the%20fix)&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/8414)
Author
Owner

@bytaesu commented on GitHub (Mar 5, 2026):

Hi @Abdelkader-gnichi, we are looking into this

@bytaesu commented on GitHub (Mar 5, 2026): Hi @Abdelkader-gnichi, we are looking into this
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2995