Unable to invite member to organization - Invalid body parameters #2242

Closed
opened 2026-03-13 09:37:48 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @MakakWasTaken on GitHub (Oct 31, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

const body = {
	email: formData.get("email")?.toString() as string,
	role: formData.get("role")?.toString() as ROLE,
	customerId: formData.get("customerId")?.toString() ?? (undefined as string | undefined),
};

await auth.api.createInvitation({
	body,
	headers: await headers(),
});

Current vs. Expected behavior

When I try to call the auth.api.createInvitation method I get Invalid body parameters. The problem with providing additional error on the issue is that the VALIDATION_ERROR does not give a stacktrace that describes what adapter call gives the error in the createInvitation method

What version of Better Auth are you using?

1.4.0-beta.15

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.0.0: Wed Sep 17 21:41:50 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6030",
    "release": "25.0.0",
    "cpuCount": 11,
    "cpuModel": "Apple M3 Pro",
    "totalMemory": "18.00 GB",
    "freeMemory": "2.70 GB"
  },
  "node": {
    "version": "v22.21.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.4"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "^16.0.1"
    },
    {
      "name": "react",
      "version": "^19.2.0"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.16.3"
    }
  ],
  "betterAuth": {
    "version": "^1.4.0-beta.15",
    "config": {
      "appName": "Biolens",
      "trustedOrigins": [
        "http://localhost:3000"
      ],
      "socialProviders": {
        "microsoft": {
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]",
          "tenantId": "common",
          "authority": "https://login.microsoftonline.com",
          "prompt": "select_account"
        }
      },
      "advanced": {
        "crossSubDomainCookies": {
          "enabled": true,
          "domain": "localhost"
        }
      },
      "session": {
        "additionalFields": {
          "activeOrganizationRole": {
            "type": "string",
            "nullable": true,
            "required": false
          },
          "activeOrganizationCustomerId": {
            "type": "string",
            "nullable": true,
            "required": false
          }
        },
        "expiresIn": 604800,
        "cookieCache": {
          "enabled": true,
          "maxAge": 2592000,
          "strategy": "jwe",
          "refreshCache": true
        }
      },
      "database": {
        "_events": {},
        "_eventsCount": 0,
        "options": {
          "connectionString": "[REDACTED]",
          "max": 10,
          "min": 0,
          "maxUses": null,
          "allowExitOnIdle": false,
          "maxLifetimeSeconds": 0,
          "idleTimeoutMillis": 10000
        },
        "_clients": [],
        "_idle": [],
        "_expired": {},
        "_pendingQueue": [],
        "ending": false,
        "ended": false
      },
      "databaseHooks": {
        "session": {
          "update": {}
        }
      },
      "user": {
        "additionalFields": {
          "image": {
            "type": "string",
            "returned": false
          }
        }
      },
      "plugins": [
        {
          "name": "admin",
          "config": {
            "id": "admin",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "FAILED_TO_CREATE_USER": "Failed to create user",
              "USER_ALREADY_EXISTS": "User already exists.",
              "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.",
              "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself",
              "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users",
              "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users",
              "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users",
              "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]",
              "BANNED_USER": "You have been banned from this application",
              "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user",
              "NO_DATA_TO_UPDATE": "No data to update",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users",
              "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself",
              "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value"
            },
            "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": "passkey",
          "config": {
            "id": "passkey",
            "endpoints": {},
            "schema": {
              "passkey": {
                "fields": {
                  "name": {
                    "type": "string",
                    "required": false
                  },
                  "publicKey": {
                    "type": "string",
                    "required": true
                  },
                  "userId": {
                    "type": "string",
                    "references": {
                      "model": "user",
                      "field": "id"
                    },
                    "required": true
                  },
                  "credentialID": {
                    "type": "string",
                    "required": true
                  },
                  "counter": {
                    "type": "number",
                    "required": true
                  },
                  "deviceType": {
                    "type": "string",
                    "required": true
                  },
                  "backedUp": {
                    "type": "boolean",
                    "required": true
                  },
                  "transports": {
                    "type": "string",
                    "required": false
                  },
                  "createdAt": {
                    "type": "date",
                    "required": false
                  },
                  "aaguid": {
                    "type": "string",
                    "required": false
                  }
                }
              }
            },
            "$ERROR_CODES": {
              "CHALLENGE_NOT_FOUND": "Challenge not found",
              "YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY": "You are not allowed to register this passkey",
              "FAILED_TO_VERIFY_REGISTRATION": "Failed to verify registration",
              "PASSKEY_NOT_FOUND": "Passkey not found",
              "AUTHENTICATION_FAILED": "Authentication failed",
              "UNABLE_TO_CREATE_SESSION": "Unable to create session",
              "FAILED_TO_UPDATE_PASSKEY": "Failed to update passkey"
            }
          }
        },
        {
          "name": "last-login-method",
          "config": {
            "id": "last-login-method",
            "hooks": {
              "after": [
                {}
              ]
            }
          }
        },
        {
          "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
                  },
                  "logo": {
                    "type": "string",
                    "required": false
                  },
                  "createdAt": {
                    "type": "date",
                    "required": true
                  },
                  "metadata": {
                    "type": "string",
                    "required": false
                  },
                  "contactPerson": {
                    "type": "string",
                    "defaultValue": ""
                  },
                  "phoneNumber": {
                    "type": "string",
                    "defaultValue": ""
                  },
                  "street": {
                    "type": "string",
                    "defaultValue": ""
                  },
                  "postalCode": {
                    "type": "string",
                    "defaultValue": ""
                  },
                  "city": {
                    "type": "string",
                    "defaultValue": ""
                  },
                  "country": {
                    "type": "string",
                    "defaultValue": ""
                  }
                }
              },
              "member": {
                "fields": {
                  "organizationId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "organization",
                      "field": "id"
                    }
                  },
                  "userId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "user",
                      "field": "id"
                    }
                  },
                  "role": {
                    "type": "string",
                    "required": true,
                    "sortable": true,
                    "defaultValue": "member"
                  },
                  "createdAt": {
                    "type": "date",
                    "required": true
                  },
                  "customerId": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "invitation": {
                "fields": {
                  "organizationId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "organization",
                      "field": "id"
                    }
                  },
                  "email": {
                    "type": "string",
                    "required": true,
                    "sortable": 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
                  },
                  "customerId": {
                    "type": "string",
                    "nullable": 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": "You are not allowed to create a new organization",
              "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS": "You have reached the maximum number of organizations",
              "ORGANIZATION_ALREADY_EXISTS": "Organization already exists",
              "ORGANIZATION_SLUG_ALREADY_TAKEN": "Organization slug already taken",
              "ORGANIZATION_NOT_FOUND": "Organization not found",
              "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION": "User is not a member of the organization",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION": "You are not allowed to update this organization",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION": "You are not allowed to delete this organization",
              "NO_ACTIVE_ORGANIZATION": "No active organization",
              "USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION": "User is already a member of this organization",
              "MEMBER_NOT_FOUND": "Member not found",
              "ROLE_NOT_FOUND": "Role not found",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM": "You are not allowed to create a new team",
              "TEAM_ALREADY_EXISTS": "Team already exists",
              "TEAM_NOT_FOUND": "Team not found",
              "YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER": "You cannot leave the organization as the only owner",
              "YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER": "You cannot leave the organization without an owner",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER": "You are not allowed to delete this member",
              "YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION": "You are not allowed to invite users to this organization",
              "USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION": "User is already invited to this organization",
              "INVITATION_NOT_FOUND": "Invitation not found",
              "YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION": "You are not the recipient of the invitation",
              "EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION": "Email verification required before accepting or rejecting invitation",
              "YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION": "You are not allowed to cancel this invitation",
              "INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION": "Inviter is no longer a member of the organization",
              "YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE": "You are not allowed to invite a user with this role",
              "FAILED_TO_RETRIEVE_INVITATION": "Failed to retrieve invitation",
              "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS": "You have reached the maximum number of teams",
              "UNABLE_TO_REMOVE_LAST_TEAM": "Unable to remove last team",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER": "You are not allowed to update this member",
              "ORGANIZATION_MEMBERSHIP_LIMIT_REACHED": "Organization membership limit reached",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION": "You are not allowed to create teams in this organization",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION": "You are not allowed to delete teams in this organization",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM": "You are not allowed to update this team",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM": "You are not allowed to delete this team",
              "INVITATION_LIMIT_REACHED": "Invitation limit reached",
              "TEAM_MEMBER_LIMIT_REACHED": "Team member limit reached",
              "USER_IS_NOT_A_MEMBER_OF_THE_TEAM": "User is not a member of the team",
              "YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM": "You are not allowed to list the members of this team",
              "YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM": "You do not have an active team",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER": "You are not allowed to create a new member",
              "YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER": "You are not allowed to remove a team member",
              "YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION": "You are not allowed to access this organization as an owner",
              "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION": "You are not a member of this organization",
              "MISSING_AC_INSTANCE": "Dynamic Access Control requires a pre-defined ac instance on the server auth plugin. Read server logs for more information",
              "YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE": "You must be in an organization to create a role",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE": "You are not allowed to create a role",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE": "You are not allowed to update a role",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE": "You are not allowed to delete a role",
              "YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE": "You are not allowed to read a role",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE": "You are not allowed to list a role",
              "YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE": "You are not allowed to get a role",
              "TOO_MANY_ROLES": "This organization has too many roles",
              "INVALID_RESOURCE": "The provided permission includes an invalid resource",
              "ROLE_NAME_IS_ALREADY_TAKEN": "That role name is already taken",
              "CANNOT_DELETE_A_PRE_DEFINED_ROLE": "Cannot delete a pre-defined role"
            },
            "options": {
              "roles": {
                "admin": {
                  "statements": {
                    "organization": [
                      "update"
                    ],
                    "invitation": [
                      "create",
                      "cancel"
                    ],
                    "member": [
                      "create",
                      "update",
                      "delete"
                    ],
                    "team": [
                      "create",
                      "update",
                      "delete"
                    ],
                    "ac": [
                      "create",
                      "read",
                      "update",
                      "delete"
                    ]
                  }
                },
                "owner": {
                  "statements": {
                    "organization": [
                      "update",
                      "delete"
                    ],
                    "member": [
                      "create",
                      "update",
                      "delete"
                    ],
                    "invitation": [
                      "create",
                      "cancel"
                    ],
                    "team": [
                      "create",
                      "update",
                      "delete"
                    ],
                    "ac": [
                      "create",
                      "read",
                      "update",
                      "delete"
                    ]
                  }
                },
                "customer": {
                  "statements": {
                    "organization": [],
                    "member": [],
                    "invitation": [],
                    "team": [],
                    "ac": [
                      "read"
                    ]
                  }
                }
              },
              "creatorRole": "owner",
              "organizationHooks": {},
              "schema": {
                "member": {
                  "additionalFields": {
                    "customerId": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "invitation": {
                  "additionalFields": {
                    "customerId": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "organization": {
                  "additionalFields": {
                    "contactPerson": {
                      "type": "string",
                      "defaultValue": ""
                    },
                    "phoneNumber": {
                      "type": "string",
                      "defaultValue": ""
                    },
                    "street": {
                      "type": "string",
                      "defaultValue": ""
                    },
                    "postalCode": {
                      "type": "string",
                      "defaultValue": ""
                    },
                    "city": {
                      "type": "string",
                      "defaultValue": ""
                    },
                    "country": {
                      "type": "string",
                      "defaultValue": ""
                    },
                  }
                }
              }
            }
          }
        },
        {
          "name": "next-cookies",
          "config": {
            "id": "next-cookies",
            "hooks": {
              "after": [
                {}
              ]
            }
          }
        }
      ]
    }
  }
}

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

Client, Backend

Auth config (if applicable)

import { ROLE } from '@misc/roles'
import { betterAuth } from 'better-auth'
import { nextCookies } from 'better-auth/next-js'
import {
  type Member as AuthMember,
  admin,
  lastLoginMethod,
  organization,
} from 'better-auth/plugins'
import {
  adminAc,
  memberAc,
  ownerAc,
} from 'better-auth/plugins/organization/access'
import { passkey } from 'better-auth/plugins/passkey'
import { Pool } from 'pg'

const isProduction = process.env.NODE_ENV === 'production'

export const auth = betterAuth({
  appName: 'Awesome Project',
  trustedOrigins: ['http://localhost:3000'],
  socialProviders: {
    microsoft: {
      clientId: process.env.MICROSOFT_CLIENT_ID as string,
      clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,
      // Optional
      tenantId: 'common',
      authority: 'https://login.microsoftonline.com', // Authentication authority URL
      prompt: 'select_account', // Forces account selection
    },
  },
  advanced: {
    crossSubDomainCookies: {
      enabled: true,
      domain: process.env.BETTER_AUTH_COOKIE_DOMAIN as string,
    },
  },
  session: {
    additionalFields: {
      activeOrganizationRole: {
        type: 'string',
        nullable: true,
        required: false,
      },
      activeOrganizationCustomerId: {
        type: 'string',
        nullable: true,
        required: false,
      },
    },
    expiresIn: 60 * 60 * 24 * 7, // 7 days
    cookieCache: {
      enabled: true,
      maxAge: 30 * 24 * 60 * 60, // 30 days cache duration
      strategy: 'jwe', // Use encrypted tokens for better security
      refreshCache: true, // Enable stateless refresh
    },
  },
  database: new Pool({
    connectionString: process.env.DATABASE_URL,
  }),
  databaseHooks: {
    session: {
      update: {
        before: async (session, context) => {
          // When updating the session (setActiveOrganization), we also want to add the activeOrganizationRole
          if (!context) {
            throw new Error('No context defined')
          }

          const prevSession = context.context.session

          if (!prevSession?.user.id || !session.activeOrganizationId) {
            throw new Error('Missing session data to perform action')
          }

          const member = await context.context.adapter.findOne<
            AuthMember & {
              customerId: string
              plantId: string
            }
          >({
            model: 'member',
            where: [
              {
                field: 'userId',
                value: prevSession.user.id,
              },
              {
                field: 'organizationId',
                value: session.activeOrganizationId as string,
              },
            ],
          })

          if (!member) {
            throw new Error('User is not a part of this team')
          }

          return {
            data: {
              ...session,
              activeOrganizationRole: member.role,
              activeOrganizationCustomerId: member.customerId,
            },
          }
        },
      },
    },
  },
  user: {
    additionalFields: {
      image: {
        type: 'string',
        returned: false,
      },
    },
  },
  plugins: [
    admin(),
    passkey(),
    lastLoginMethod(),
    organization({
      roles: {
        [ROLE.ADMIN]: adminAc,
        [ROLE.OWNER]: ownerAc,
        [ROLE.CUSTOMER]: memberAc,
      },
      allowUserToCreateOrganization: (user) => user.role === 'admin', // Only admins can create organizations
      creatorRole: ROLE.OWNER,
      schema: {
        member: {
          additionalFields: {
            customerId: {
              type: 'string',
              nullable: true,
            },
          },
        },
        invitation: {
          additionalFields: {
            customerId: {
              type: 'string',
              nullable: true,
            },
          },
        },
        organization: {
          additionalFields: {
            contactPerson: {
              type: 'string',
              defaultValue: '',
            },
            phoneNumber: {
              type: 'string',
              defaultValue: '',
            },
            street: {
              type: 'string',
              defaultValue: '',
            },
            postalCode: {
              type: 'string',
              defaultValue: '',
            },
            city: {
              type: 'string',
              defaultValue: '',
            },
            country: {
              type: 'string',
              defaultValue: '',
            }
          },
        },
      },
    }),
    nextCookies(),
  ],
})

export type Member = typeof auth.$Infer.Member & {
  // better-auth is currently unable to infer additional fields on relations
  // https://github.com/better-auth/better-auth/pull/3731 See this PR for more info
  customerId?: string
}
export type Session = typeof auth.$Infer.Session
export type Organization = typeof auth.$Infer.ActiveOrganization

Additional context

The error happens with both authClient and auth. It would also be nice for future debugging that the VALIDATION_ERROR gave a better description of where the issue happened.

Originally created by @MakakWasTaken on GitHub (Oct 31, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce ``` const body = { email: formData.get("email")?.toString() as string, role: formData.get("role")?.toString() as ROLE, customerId: formData.get("customerId")?.toString() ?? (undefined as string | undefined), }; await auth.api.createInvitation({ body, headers: await headers(), }); ``` ### Current vs. Expected behavior When I try to call the `auth.api.createInvitation` method I get `Invalid body parameters`. The problem with providing additional error on the issue is that the `VALIDATION_ERROR` does not give a stacktrace that describes what adapter call gives the error in the `createInvitation` method ### What version of Better Auth are you using? 1.4.0-beta.15 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.0.0: Wed Sep 17 21:41:50 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6030", "release": "25.0.0", "cpuCount": 11, "cpuModel": "Apple M3 Pro", "totalMemory": "18.00 GB", "freeMemory": "2.70 GB" }, "node": { "version": "v22.21.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.4" }, "frameworks": [ { "name": "next", "version": "^16.0.1" }, { "name": "react", "version": "^19.2.0" } ], "databases": [ { "name": "pg", "version": "^8.16.3" } ], "betterAuth": { "version": "^1.4.0-beta.15", "config": { "appName": "Biolens", "trustedOrigins": [ "http://localhost:3000" ], "socialProviders": { "microsoft": { "clientId": "[REDACTED]", "clientSecret": "[REDACTED]", "tenantId": "common", "authority": "https://login.microsoftonline.com", "prompt": "select_account" } }, "advanced": { "crossSubDomainCookies": { "enabled": true, "domain": "localhost" } }, "session": { "additionalFields": { "activeOrganizationRole": { "type": "string", "nullable": true, "required": false }, "activeOrganizationCustomerId": { "type": "string", "nullable": true, "required": false } }, "expiresIn": 604800, "cookieCache": { "enabled": true, "maxAge": 2592000, "strategy": "jwe", "refreshCache": true } }, "database": { "_events": {}, "_eventsCount": 0, "options": { "connectionString": "[REDACTED]", "max": 10, "min": 0, "maxUses": null, "allowExitOnIdle": false, "maxLifetimeSeconds": 0, "idleTimeoutMillis": 10000 }, "_clients": [], "_idle": [], "_expired": {}, "_pendingQueue": [], "ending": false, "ended": false }, "databaseHooks": { "session": { "update": {} } }, "user": { "additionalFields": { "image": { "type": "string", "returned": false } } }, "plugins": [ { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": "Failed to create user", "USER_ALREADY_EXISTS": "User already exists.", "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.", "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself", "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role", "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions", "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users", "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users", "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions", "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users", "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]", "BANNED_USER": "You have been banned from this application", "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user", "NO_DATA_TO_UPDATE": "No data to update", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users", "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself", "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value" }, "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": "passkey", "config": { "id": "passkey", "endpoints": {}, "schema": { "passkey": { "fields": { "name": { "type": "string", "required": false }, "publicKey": { "type": "string", "required": true }, "userId": { "type": "string", "references": { "model": "user", "field": "id" }, "required": true }, "credentialID": { "type": "string", "required": true }, "counter": { "type": "number", "required": true }, "deviceType": { "type": "string", "required": true }, "backedUp": { "type": "boolean", "required": true }, "transports": { "type": "string", "required": false }, "createdAt": { "type": "date", "required": false }, "aaguid": { "type": "string", "required": false } } } }, "$ERROR_CODES": { "CHALLENGE_NOT_FOUND": "Challenge not found", "YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY": "You are not allowed to register this passkey", "FAILED_TO_VERIFY_REGISTRATION": "Failed to verify registration", "PASSKEY_NOT_FOUND": "Passkey not found", "AUTHENTICATION_FAILED": "Authentication failed", "UNABLE_TO_CREATE_SESSION": "Unable to create session", "FAILED_TO_UPDATE_PASSKEY": "Failed to update passkey" } } }, { "name": "last-login-method", "config": { "id": "last-login-method", "hooks": { "after": [ {} ] } } }, { "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 }, "logo": { "type": "string", "required": false }, "createdAt": { "type": "date", "required": true }, "metadata": { "type": "string", "required": false }, "contactPerson": { "type": "string", "defaultValue": "" }, "phoneNumber": { "type": "string", "defaultValue": "" }, "street": { "type": "string", "defaultValue": "" }, "postalCode": { "type": "string", "defaultValue": "" }, "city": { "type": "string", "defaultValue": "" }, "country": { "type": "string", "defaultValue": "" } } }, "member": { "fields": { "organizationId": { "type": "string", "required": true, "references": { "model": "organization", "field": "id" } }, "userId": { "type": "string", "required": true, "references": { "model": "user", "field": "id" } }, "role": { "type": "string", "required": true, "sortable": true, "defaultValue": "member" }, "createdAt": { "type": "date", "required": true }, "customerId": { "type": "string", "nullable": true } } }, "invitation": { "fields": { "organizationId": { "type": "string", "required": true, "references": { "model": "organization", "field": "id" } }, "email": { "type": "string", "required": true, "sortable": 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 }, "customerId": { "type": "string", "nullable": 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": "You are not allowed to create a new organization", "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS": "You have reached the maximum number of organizations", "ORGANIZATION_ALREADY_EXISTS": "Organization already exists", "ORGANIZATION_SLUG_ALREADY_TAKEN": "Organization slug already taken", "ORGANIZATION_NOT_FOUND": "Organization not found", "USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION": "User is not a member of the organization", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION": "You are not allowed to update this organization", "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION": "You are not allowed to delete this organization", "NO_ACTIVE_ORGANIZATION": "No active organization", "USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION": "User is already a member of this organization", "MEMBER_NOT_FOUND": "Member not found", "ROLE_NOT_FOUND": "Role not found", "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM": "You are not allowed to create a new team", "TEAM_ALREADY_EXISTS": "Team already exists", "TEAM_NOT_FOUND": "Team not found", "YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER": "You cannot leave the organization as the only owner", "YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER": "You cannot leave the organization without an owner", "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER": "You are not allowed to delete this member", "YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION": "You are not allowed to invite users to this organization", "USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION": "User is already invited to this organization", "INVITATION_NOT_FOUND": "Invitation not found", "YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION": "You are not the recipient of the invitation", "EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION": "Email verification required before accepting or rejecting invitation", "YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION": "You are not allowed to cancel this invitation", "INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION": "Inviter is no longer a member of the organization", "YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE": "You are not allowed to invite a user with this role", "FAILED_TO_RETRIEVE_INVITATION": "Failed to retrieve invitation", "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS": "You have reached the maximum number of teams", "UNABLE_TO_REMOVE_LAST_TEAM": "Unable to remove last team", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER": "You are not allowed to update this member", "ORGANIZATION_MEMBERSHIP_LIMIT_REACHED": "Organization membership limit reached", "YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION": "You are not allowed to create teams in this organization", "YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION": "You are not allowed to delete teams in this organization", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM": "You are not allowed to update this team", "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM": "You are not allowed to delete this team", "INVITATION_LIMIT_REACHED": "Invitation limit reached", "TEAM_MEMBER_LIMIT_REACHED": "Team member limit reached", "USER_IS_NOT_A_MEMBER_OF_THE_TEAM": "User is not a member of the team", "YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM": "You are not allowed to list the members of this team", "YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM": "You do not have an active team", "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER": "You are not allowed to create a new member", "YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER": "You are not allowed to remove a team member", "YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION": "You are not allowed to access this organization as an owner", "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION": "You are not a member of this organization", "MISSING_AC_INSTANCE": "Dynamic Access Control requires a pre-defined ac instance on the server auth plugin. Read server logs for more information", "YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE": "You must be in an organization to create a role", "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE": "You are not allowed to create a role", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE": "You are not allowed to update a role", "YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE": "You are not allowed to delete a role", "YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE": "You are not allowed to read a role", "YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE": "You are not allowed to list a role", "YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE": "You are not allowed to get a role", "TOO_MANY_ROLES": "This organization has too many roles", "INVALID_RESOURCE": "The provided permission includes an invalid resource", "ROLE_NAME_IS_ALREADY_TAKEN": "That role name is already taken", "CANNOT_DELETE_A_PRE_DEFINED_ROLE": "Cannot delete a pre-defined role" }, "options": { "roles": { "admin": { "statements": { "organization": [ "update" ], "invitation": [ "create", "cancel" ], "member": [ "create", "update", "delete" ], "team": [ "create", "update", "delete" ], "ac": [ "create", "read", "update", "delete" ] } }, "owner": { "statements": { "organization": [ "update", "delete" ], "member": [ "create", "update", "delete" ], "invitation": [ "create", "cancel" ], "team": [ "create", "update", "delete" ], "ac": [ "create", "read", "update", "delete" ] } }, "customer": { "statements": { "organization": [], "member": [], "invitation": [], "team": [], "ac": [ "read" ] } } }, "creatorRole": "owner", "organizationHooks": {}, "schema": { "member": { "additionalFields": { "customerId": { "type": "string", "nullable": true } } }, "invitation": { "additionalFields": { "customerId": { "type": "string", "nullable": true } } }, "organization": { "additionalFields": { "contactPerson": { "type": "string", "defaultValue": "" }, "phoneNumber": { "type": "string", "defaultValue": "" }, "street": { "type": "string", "defaultValue": "" }, "postalCode": { "type": "string", "defaultValue": "" }, "city": { "type": "string", "defaultValue": "" }, "country": { "type": "string", "defaultValue": "" }, } } } } } }, { "name": "next-cookies", "config": { "id": "next-cookies", "hooks": { "after": [ {} ] } } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Client, Backend ### Auth config (if applicable) ```typescript import { ROLE } from '@misc/roles' import { betterAuth } from 'better-auth' import { nextCookies } from 'better-auth/next-js' import { type Member as AuthMember, admin, lastLoginMethod, organization, } from 'better-auth/plugins' import { adminAc, memberAc, ownerAc, } from 'better-auth/plugins/organization/access' import { passkey } from 'better-auth/plugins/passkey' import { Pool } from 'pg' const isProduction = process.env.NODE_ENV === 'production' export const auth = betterAuth({ appName: 'Awesome Project', trustedOrigins: ['http://localhost:3000'], socialProviders: { microsoft: { clientId: process.env.MICROSOFT_CLIENT_ID as string, clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string, // Optional tenantId: 'common', authority: 'https://login.microsoftonline.com', // Authentication authority URL prompt: 'select_account', // Forces account selection }, }, advanced: { crossSubDomainCookies: { enabled: true, domain: process.env.BETTER_AUTH_COOKIE_DOMAIN as string, }, }, session: { additionalFields: { activeOrganizationRole: { type: 'string', nullable: true, required: false, }, activeOrganizationCustomerId: { type: 'string', nullable: true, required: false, }, }, expiresIn: 60 * 60 * 24 * 7, // 7 days cookieCache: { enabled: true, maxAge: 30 * 24 * 60 * 60, // 30 days cache duration strategy: 'jwe', // Use encrypted tokens for better security refreshCache: true, // Enable stateless refresh }, }, database: new Pool({ connectionString: process.env.DATABASE_URL, }), databaseHooks: { session: { update: { before: async (session, context) => { // When updating the session (setActiveOrganization), we also want to add the activeOrganizationRole if (!context) { throw new Error('No context defined') } const prevSession = context.context.session if (!prevSession?.user.id || !session.activeOrganizationId) { throw new Error('Missing session data to perform action') } const member = await context.context.adapter.findOne< AuthMember & { customerId: string plantId: string } >({ model: 'member', where: [ { field: 'userId', value: prevSession.user.id, }, { field: 'organizationId', value: session.activeOrganizationId as string, }, ], }) if (!member) { throw new Error('User is not a part of this team') } return { data: { ...session, activeOrganizationRole: member.role, activeOrganizationCustomerId: member.customerId, }, } }, }, }, }, user: { additionalFields: { image: { type: 'string', returned: false, }, }, }, plugins: [ admin(), passkey(), lastLoginMethod(), organization({ roles: { [ROLE.ADMIN]: adminAc, [ROLE.OWNER]: ownerAc, [ROLE.CUSTOMER]: memberAc, }, allowUserToCreateOrganization: (user) => user.role === 'admin', // Only admins can create organizations creatorRole: ROLE.OWNER, schema: { member: { additionalFields: { customerId: { type: 'string', nullable: true, }, }, }, invitation: { additionalFields: { customerId: { type: 'string', nullable: true, }, }, }, organization: { additionalFields: { contactPerson: { type: 'string', defaultValue: '', }, phoneNumber: { type: 'string', defaultValue: '', }, street: { type: 'string', defaultValue: '', }, postalCode: { type: 'string', defaultValue: '', }, city: { type: 'string', defaultValue: '', }, country: { type: 'string', defaultValue: '', } }, }, }, }), nextCookies(), ], }) export type Member = typeof auth.$Infer.Member & { // better-auth is currently unable to infer additional fields on relations // https://github.com/better-auth/better-auth/pull/3731 See this PR for more info customerId?: string } export type Session = typeof auth.$Infer.Session export type Organization = typeof auth.$Infer.ActiveOrganization ``` ### Additional context The error happens with both authClient and auth. It would also be nice for future debugging that the VALIDATION_ERROR gave a better description of where the issue happened.
Author
Owner

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

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

The "Invalid body parameters" error with createInvitation usually means the request body doesn't match the expected schema. For Better Auth, the required fields are email, role, and organizationId. Optional fields like customerId should be omitted if not set, or explicitly set to null—passing undefined can cause validation to fail. Also, make sure the role value is a valid string (like "member", "admin", or "owner"), not a TypeScript enum or an invalid string, as runtime values matter, not type assertions.

Recent changes in Better Auth have made the invitation schema stricter, requiring fields like createdAt and expiresAt to be present and valid. If these are missing, you'll get a validation error. The API does not currently provide detailed error traces for validation failures, so double-check that your client and server schemas (especially for custom fields) are perfectly in sync—any mismatch will cause this error. See this issue and this PR for more details.

Try constructing your body like this (assuming you have all required fields):

const body = {
  email: formData.get("email")?.toString(),
  role: formData.get("role")?.toString(), // must be a valid string role
  organizationId: yourOrgId, // required!
  customerId: formData.get("customerId")?.toString() || null, // omit if not set
  // expiresAt and createdAt may be required depending on your version/config
};

If you still get the error, check that your client and server both define the same invitation schema, including any custom fields. For debugging, you can log the exact body sent and compare it to the schema expected by your server. For more actionable errors, you may need to add custom error handling or logging on the server side, as Better Auth's default VALIDATION_ERROR is not very descriptive.

References:

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Oct 31, 2025): <!-- Greeting --> Hi @MakakWasTaken! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The "Invalid body parameters" error with `createInvitation` usually means the request body doesn't match the expected schema. For Better Auth, the required fields are `email`, `role`, and `organizationId`. Optional fields like `customerId` should be omitted if not set, or explicitly set to `null`—passing `undefined` can cause validation to fail. Also, make sure the `role` value is a valid string (like `"member"`, `"admin"`, or `"owner"`), not a TypeScript enum or an invalid string, as runtime values matter, not type assertions. Recent changes in Better Auth have made the invitation schema stricter, requiring fields like `createdAt` and `expiresAt` to be present and valid. If these are missing, you'll get a validation error. The API does not currently provide detailed error traces for validation failures, so double-check that your client and server schemas (especially for custom fields) are perfectly in sync—any mismatch will cause this error. See [this issue](https://github.com/better-auth/better-auth/issues/3500) and [this PR](https://github.com/better-auth/better-auth/pull/2346) for more details. Try constructing your body like this (assuming you have all required fields): ```js const body = { email: formData.get("email")?.toString(), role: formData.get("role")?.toString(), // must be a valid string role organizationId: yourOrgId, // required! customerId: formData.get("customerId")?.toString() || null, // omit if not set // expiresAt and createdAt may be required depending on your version/config }; ``` If you still get the error, check that your client and server both define the same invitation schema, including any custom fields. For debugging, you can log the exact body sent and compare it to the schema expected by your server. For more actionable errors, you may need to add custom error handling or logging on the server side, as Better Auth's default `VALIDATION_ERROR` is not very descriptive. References: - [Schema sync and validation errors](https://github.com/better-auth/better-auth/issues/3500) - [Invitation schema requirements](https://github.com/better-auth/better-auth/pull/2346) - [Example of successful invitation creation](https://github.com/better-auth/better-auth/pull/4035) <!-- 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/ddb3b9f5-c6da-46a6-834e-aa71a5165107?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/ddb3b9f5-c6da-46a6-834e-aa71a5165107?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/ddb3b9f5-c6da-46a6-834e-aa71a5165107?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/ddb3b9f5-c6da-46a6-834e-aa71a5165107?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/ddb3b9f5-c6da-46a6-834e-aa71a5165107?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/ddb3b9f5-c6da-46a6-834e-aa71a5165107?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/ddb3b9f5-c6da-46a6-834e-aa71a5165107?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/5697)
Author
Owner

@MakakWasTaken commented on GitHub (Oct 31, 2025):

@dosu I was under the impression that organizationId was optional and that it would just use the active organization id from the session if it was not provided.

However I gave the solution a try like this:

const body = {
	email: formData.get("email")?.toString() as string,
	role: formData.get("role")?.toString() as ROLE,
	organizationId: session.session.activeOrganizationId as string,
	customerId: formData.get("customerId")?.toString() ?? (undefined as string | undefined),
};

But the error persists

@MakakWasTaken commented on GitHub (Oct 31, 2025): @dosu I was under the impression that `organizationId` was optional and that it would just use the active organization id from the session if it was not provided. However I gave the solution a try like this: ```typescript const body = { email: formData.get("email")?.toString() as string, role: formData.get("role")?.toString() as ROLE, organizationId: session.session.activeOrganizationId as string, customerId: formData.get("customerId")?.toString() ?? (undefined as string | undefined), }; ``` But the error persists
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 31, 2025):

Thanks for the details! Could you share a minimal reproduction (env, exact commands, versions, and full traceback if any)? We'll investigate and circle back.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 31, 2025): Thanks for the details! Could you share a minimal reproduction (env, exact commands, versions, and full traceback if any)? We'll investigate and circle back. <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

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

You’re right that organizationId can be inferred from the session if not provided, but your error isn’t caused by that field. The most likely cause is a schema mismatch or an unexpected value in one of the fields—especially undefined values.

A few things to check:

  • For nullable fields like customerId, don’t send undefined—omit the field entirely if it’s not set, or explicitly set it to null. Sending undefined will fail validation example.
  • Make sure role is a plain string at runtime (e.g., "member", "admin", "owner"), not a TypeScript enum or an invalid value. Type assertions don’t affect runtime values reference.
  • Recent versions require fields like createdAt and expiresAt on invitations. These are usually set by the backend, but if your config or API expects them in the body, you may need to include them see schema change.
  • Double-check that your client and server configs for custom fields (like customerId) are in sync.

Try this pattern for your body:

const body = {
  email: formData.get("email")?.toString(),
  role: formData.get("role")?.toString(), // must be a valid string
  organizationId: session.session.activeOrganizationId,
  ...(formData.get("customerId") ? { customerId: formData.get("customerId")?.toString() } : {}),
  // Do NOT include fields with undefined values
};

If you still get the error, log the exact JSON body you’re sending and compare it to the expected schema. The error is almost always due to a field being present with an unexpected value (like undefined or an invalid string), or a required field missing validation details.

Let me know if cleaning up the body like this resolves it!

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Oct 31, 2025): <!-- Answer --> You’re right that organizationId can be inferred from the session if not provided, but your error isn’t caused by that field. The most likely cause is a schema mismatch or an unexpected value in one of the fields—especially undefined values. A few things to check: - For nullable fields like customerId, don’t send undefined—omit the field entirely if it’s not set, or explicitly set it to null. Sending undefined will fail validation [example](https://github.com/better-auth/better-auth/issues/4033). - Make sure role is a plain string at runtime (e.g., "member", "admin", "owner"), not a TypeScript enum or an invalid value. Type assertions don’t affect runtime values [reference](https://github.com/better-auth/better-auth/issues/4463). - Recent versions require fields like createdAt and expiresAt on invitations. These are usually set by the backend, but if your config or API expects them in the body, you may need to include them [see schema change](https://github.com/better-auth/better-auth/pull/2346). - Double-check that your client and server configs for custom fields (like customerId) are in sync. Try this pattern for your body: ```js const body = { email: formData.get("email")?.toString(), role: formData.get("role")?.toString(), // must be a valid string organizationId: session.session.activeOrganizationId, ...(formData.get("customerId") ? { customerId: formData.get("customerId")?.toString() } : {}), // Do NOT include fields with undefined values }; ``` If you still get the error, log the exact JSON body you’re sending and compare it to the expected schema. The error is almost always due to a field being present with an unexpected value (like undefined or an invalid string), or a required field missing [validation details](https://github.com/better-auth/better-auth/issues/4720). Let me know if cleaning up the body like this resolves it! <!-- 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/11eea39d-6ee0-41da-b157-49d2b0af9a57?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/11eea39d-6ee0-41da-b157-49d2b0af9a57?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/11eea39d-6ee0-41da-b157-49d2b0af9a57?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/11eea39d-6ee0-41da-b157-49d2b0af9a57?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/11eea39d-6ee0-41da-b157-49d2b0af9a57?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/11eea39d-6ee0-41da-b157-49d2b0af9a57?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/11eea39d-6ee0-41da-b157-49d2b0af9a57?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/5697)
Author
Owner

@MakakWasTaken commented on GitHub (Oct 31, 2025):

I figured out the error. The problem was that I was set up the additionalFields like this:

const additionalFields = {
       customerId: {
          type: 'string',
          nullable: true,
       },
}

However null this does not mean that null can be passed to the value due to a typescript error:
Image
This led me to believe that I could just pass undefined instead of null and it would infer the null from that. However that was not the case.
The solution was to add the required: false property as well and then just pass undefined as it did before.

I will leave this here if anybody else comes across this issue.

@MakakWasTaken commented on GitHub (Oct 31, 2025): I figured out the error. The problem was that I was set up the additionalFields like this: ```typescript const additionalFields = { customerId: { type: 'string', nullable: true, }, } ``` However null this does not mean that null can be passed to the value due to a typescript error: <img width="308" height="43" alt="Image" src="https://github.com/user-attachments/assets/625e4f77-64d8-4ce5-b766-353a0df478ab" /> This led me to believe that I could just pass undefined instead of null and it would infer the null from that. However that was not the case. The solution was to add the `required: false` property as well and then just pass undefined as it did before. I will leave this here if anybody else comes across this issue.
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 31, 2025):

Thanks for the details! Could you share a minimal reproduction (env, exact commands, versions, and full traceback if any)? We'll investigate and circle back.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 31, 2025): Thanks for the details! Could you share a minimal reproduction (env, exact commands, versions, and full traceback if any)? We'll investigate and circle back. <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2242