[GH-ISSUE #5860] [Bug Report] JSON.parse(undefined) Error When dynamicAccessControl is Enabled #18992

Closed
opened 2026-04-15 17:44:30 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @iptoux on GitHub (Nov 9, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/5860

Originally assigned to: @ping-maxwell on GitHub.

Bug Report: JSON.parse(undefined) Error When dynamicAccessControl is Enabled

Summary

When dynamicAccessControl: { enabled: true } is configured in the BetterAuth organization plugin, the /api/auth/organization/invite-member endpoint fails with a SyntaxError: "undefined" is not valid JSON error. Disabling dynamic access control resolves the issue immediately. After activating, the invitation system is completly broken.

Environment

  • BetterAuth Version: 1.3.34
  • Next.js Version: 16.0.1
  • Runtime: Node.js
  • Database: SQLite (via Prisma)
  • Prisma Version: 6.19.0

Configuration

BetterAuth Server Configuration (src/lib/auth.ts)

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@/generated/prisma/client";
import { organization } from "better-auth/plugins";
import { ac, owner, admin as adminRole, member } from "@/lib/access-control";

const prisma = new PrismaClient();

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "sqlite",
  }),
  plugins: [
    organization({
      ac,
      roles: {
        owner,
        admin: adminRole,
        member,
      },
      dynamicAccessControl: {
        enabled: true, // ❌ Causes the bug
      },
      teams: {
        enabled: true,
      },
      organizationHooks: {
        async afterCreateOrganization({ organization }) {
          // Creates static roles in OrganizationRole table
          // (owner, admin, member with proper permissions)
        },
      },
      async sendInvitationEmail(data) {
        // Custom email sending logic
      },
    }),
  ],
});

Access Control Configuration (src/lib/access-control.ts)

import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements } from "better-auth/plugins/organization/access";

const statements = {
  ...defaultStatements,
  organization: ["create", "read", "update", "delete", "manage"],
  member: ["create", "read", "update", "delete", "invite"],
  invitation: ["create", "read", "update", "delete", "cancel"],
  role: ["create", "read", "update", "delete"],
  sale: ["create", "read", "update", "delete"],
  marketing: ["create", "read", "update", "delete"],
  dashboard: ["read", "export", "manage"],
  settings: ["read", "update"],
  audit: ["read"],
} as const;

export const ac = createAccessControl(statements);

export const owner = ac.newRole({
  organization: ["create", "read", "update", "delete", "manage"],
  member: ["create", "read", "update", "delete", "invite"],
  role: ["create", "read", "update", "delete"],
  invitation: ["create", "read", "update", "delete", "cancel"],
  sale: ["create", "read", "update", "delete"],
  marketing: ["create", "read", "update", "delete"],
  dashboard: ["read", "export", "manage"],
  settings: ["read", "update"],
  audit: ["read"],
});

// Similar definitions for admin and member roles...

Database Schema

OrganizationRole Table

model OrganizationRole {
  id             String       @id @default(uuid())
  organizationId String
  organization   Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  name           String
  permissions    String       // JSON stringified permissions object
  createdAt      DateTime     @default(now())
  updatedAt      DateTime     @updatedAt

  @@unique([organizationId, name])
  @@index([organizationId])
  @@map("organization_role")
}

Member Table

model Member {
  id             String       @id
  organizationId String
  organization   Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  userId         String
  user           User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  role           String
  dynamicRoles   String?      // JSON array of dynamic organization role names
  createdAt      DateTime

  @@map("member")
}

Steps to Reproduce

  1. Configure BetterAuth with dynamicAccessControl: { enabled: true }
  2. Create an organization with static roles (owner, admin, member) stored in the OrganizationRole table
  3. Add a user as owner to the organization (Member record with role: "owner")
  4. Attempt to send an invitation via the BetterAuth endpoint:
const response = await fetch('http://localhost:3000/api/auth/organization/invite-member', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Cookie: cookieHeader, // Valid session cookie
  },
  body: JSON.stringify({
    email: '***@***.com',
    role: 'member',
    organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305',
  }),
});

Expected Behavior

The invitation should be created successfully, and the sendInvitationEmail hook should be called with the invitation data.

Actual Behavior

The endpoint returns a 500 error with the following:

2025-11-09T01:12:31.802Z ERROR [Better Auth]: SyntaxError 
SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)

# SERVER_ERROR:  SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)

POST /api/auth/organization/invite-member 500 in 441ms

Response Details

  • Status: 500 Internal Server Error
  • Response Body: Empty (no content)
  • Headers: Standard Next.js headers

Logs from Custom Invitation Route

[INVITE] 2025-11-09T01:12:31.742Z - Starting invitation process for organization: b0e914e2-38ab-403f-b1ab-3da254e90305
[INVITE] Request payload: {
  email: '***@***.com',
  role: 'member',
  teamId: 'none',
  resend: false,
  organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305'
}
[INVITE] Validating inviter permissions for user: ffyszqeZqE6tJHyiVabJyS22p9PK8nSu in organization: b0e914e2-38ab-403f-b1ab-3da254e90305
[INVITE] Inviter validation passed: {
  userId: 'ffyszqeZqE6tJHyiVabJyS22p9PK8nSu',
  organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305',
  role: 'owner'
}
[INVITE] Calling BetterAuth invite-member endpoint: {
  url: 'http://localhost:3000/api/auth/organization/invite-member',
  payload: {
    email: '***@***.com',
    role: 'member',
    organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305'
  },
  cookieHeaderLength: 304,
  hasSessionCookie: true
}
[INVITE] BetterAuth response received: {
  status: 500,
  statusText: 'Internal Server Error',
  headers: {...},
  organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305',
  userId: 'ffyszqeZqE6tJHyiVabJyS22p9PK8nSu'
}
[INVITE] BetterAuth raw response body: 
[INVITE] Could not parse BetterAuth error response as JSON: {
  responseText: '',
  organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305',
  userId: 'ffyszqeZqE6tJHyiVabJyS22p9PK8nSu'
}

Database State Verification

We verified that all data is correctly structured:

Member Record

{
  "id": "37a97061-ebdc-4fdf-8c8d-d98b62d407cf",
  "role": "owner",
  "dynamicRoles": null,
  "userName": "Admin",
  "orgName": "Test Software"
}

OrganizationRole Record (owner)

{
  "name": "owner",
  "permissions": "{\"organization\":[\"create\",\"read\",\"update\",\"delete\",\"manage\"],\"member\":[\"create\",\"read\",\"update\",\"delete\",\"invite\"],\"role\":[\"create\",\"read\",\"update\",\"delete\"],\"invitation\":[\"create\",\"read\",\"update\",\"delete\",\"cancel\"],\"sale\":[\"create\",\"read\",\"update\",\"delete\"],\"marketing\":[\"create\",\"read\",\"update\",\"delete\"],\"dashboard\":[\"read\",\"export\",\"manage\"],\"settings\":[\"read\",\"update\"],\"audit\":[\"read\"]}"
}

Verification:

  • Member record exists with valid role
  • OrganizationRole record exists for "owner"
  • Permissions JSON is valid and parseable
  • Member has invitation:create permission
  • dynamicRoles field is null (not undefined)

Workaround

Disabling dynamic access control resolves the issue completely:

organization({
  ac,
  roles: { owner, admin: adminRole, member },
  dynamicAccessControl: {
    enabled: false, // ✅ Works perfectly
  },
  // ... rest of config
})

With enabled: false, invitations are sent successfully without any errors.

Analysis

The error occurs inside BetterAuth's code when dynamicAccessControl is enabled. Based on our investigation:

  1. The error happens before the sendInvitationEmail hook is called
  2. All database data is valid - no undefined or malformed JSON
  3. The error is consistent - happens on every invitation attempt with dynamic AC enabled
  4. Cookies are correctly passed - session is valid and authenticated
  5. The response body is empty - BetterAuth returns 500 with no error details

Hypothesis

When dynamicAccessControl is enabled, BetterAuth attempts to:

  1. Load the inviter's member record
  2. Load the inviter's role from the OrganizationRole table
  3. Parse the permissions JSON
  4. Somewhere in this process, it tries to parse a field that is undefined

The error "undefined" is not valid JSON suggests that BetterAuth is calling JSON.parse() on a value that is literally the string "undefined" or an actual undefined value.

Possible Root Causes

  1. BetterAuth expects a field that doesn't exist in the Member or OrganizationRole table
  2. BetterAuth tries to parse dynamicRoles when it's null and converts it incorrectly
  3. BetterAuth's internal query returns undefined for a field it expects to exist
  4. Serialization issue where undefined is converted to the string "undefined"

Impact

  • Severity: High - Completely blocks invitation functionality when dynamic access control is enabled
  • Workaround Available: Yes - disable dynamic access control
  • Data Loss: No
  • Security Impact: None

Additional Context

  • The issue was discovered after implementing all static roles in the OrganizationRole table as required by dynamic access control
  • The access control configuration matches the database permissions exactly
  • The issue persists even after server restarts and cache clearing
  • The issue occurs in both development and production builds

Requested Fix

Please investigate why BetterAuth attempts to parse undefined when dynamicAccessControl is enabled, and ensure that:

  1. All fields are properly checked for null/undefined before parsing
  2. Error messages include more details about which field caused the parse error
  3. The response body includes error details even when internal errors occur

Thank you for your attention to this issue! We're happy to provide additional information or test fixes if needed.

Originally created by @iptoux on GitHub (Nov 9, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/5860 Originally assigned to: @ping-maxwell on GitHub. # Bug Report: `JSON.parse(undefined)` Error When `dynamicAccessControl` is Enabled ## Summary When `dynamicAccessControl: { enabled: true }` is configured in the BetterAuth organization plugin, the `/api/auth/organization/invite-member` endpoint fails with a `SyntaxError: "undefined" is not valid JSON` error. Disabling dynamic access control resolves the issue immediately. After activating, the invitation system is completly broken. ## Environment - **BetterAuth Version:** `1.3.34` - **Next.js Version:** `16.0.1` - **Runtime:** Node.js - **Database:** SQLite (via Prisma) - **Prisma Version:** `6.19.0` ## Configuration ### BetterAuth Server Configuration (`src/lib/auth.ts`) ```typescript import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { PrismaClient } from "@/generated/prisma/client"; import { organization } from "better-auth/plugins"; import { ac, owner, admin as adminRole, member } from "@/lib/access-control"; const prisma = new PrismaClient(); export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "sqlite", }), plugins: [ organization({ ac, roles: { owner, admin: adminRole, member, }, dynamicAccessControl: { enabled: true, // ❌ Causes the bug }, teams: { enabled: true, }, organizationHooks: { async afterCreateOrganization({ organization }) { // Creates static roles in OrganizationRole table // (owner, admin, member with proper permissions) }, }, async sendInvitationEmail(data) { // Custom email sending logic }, }), ], }); ``` ### Access Control Configuration (`src/lib/access-control.ts`) ```typescript import { createAccessControl } from "better-auth/plugins/access"; import { defaultStatements } from "better-auth/plugins/organization/access"; const statements = { ...defaultStatements, organization: ["create", "read", "update", "delete", "manage"], member: ["create", "read", "update", "delete", "invite"], invitation: ["create", "read", "update", "delete", "cancel"], role: ["create", "read", "update", "delete"], sale: ["create", "read", "update", "delete"], marketing: ["create", "read", "update", "delete"], dashboard: ["read", "export", "manage"], settings: ["read", "update"], audit: ["read"], } as const; export const ac = createAccessControl(statements); export const owner = ac.newRole({ organization: ["create", "read", "update", "delete", "manage"], member: ["create", "read", "update", "delete", "invite"], role: ["create", "read", "update", "delete"], invitation: ["create", "read", "update", "delete", "cancel"], sale: ["create", "read", "update", "delete"], marketing: ["create", "read", "update", "delete"], dashboard: ["read", "export", "manage"], settings: ["read", "update"], audit: ["read"], }); // Similar definitions for admin and member roles... ``` ## Database Schema ### OrganizationRole Table ```prisma model OrganizationRole { id String @id @default(uuid()) organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String permissions String // JSON stringified permissions object createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, name]) @@index([organizationId]) @@map("organization_role") } ``` ### Member Table ```prisma model Member { id String @id organizationId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) role String dynamicRoles String? // JSON array of dynamic organization role names createdAt DateTime @@map("member") } ``` ## Steps to Reproduce 1. **Configure BetterAuth** with `dynamicAccessControl: { enabled: true }` 2. **Create an organization** with static roles (owner, admin, member) stored in the `OrganizationRole` table 3. **Add a user as owner** to the organization (Member record with `role: "owner"`) 4. **Attempt to send an invitation** via the BetterAuth endpoint: ```typescript const response = await fetch('http://localhost:3000/api/auth/organization/invite-member', { method: 'POST', headers: { 'Content-Type': 'application/json', Cookie: cookieHeader, // Valid session cookie }, body: JSON.stringify({ email: '***@***.com', role: 'member', organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305', }), }); ``` ## Expected Behavior The invitation should be created successfully, and the `sendInvitationEmail` hook should be called with the invitation data. ## Actual Behavior The endpoint returns a 500 error with the following: ``` 2025-11-09T01:12:31.802Z ERROR [Better Auth]: SyntaxError SyntaxError: "undefined" is not valid JSON at JSON.parse (<anonymous>) # SERVER_ERROR: SyntaxError: "undefined" is not valid JSON at JSON.parse (<anonymous>) POST /api/auth/organization/invite-member 500 in 441ms ``` ### Response Details - **Status:** `500 Internal Server Error` - **Response Body:** Empty (no content) - **Headers:** Standard Next.js headers ### Logs from Custom Invitation Route ``` [INVITE] 2025-11-09T01:12:31.742Z - Starting invitation process for organization: b0e914e2-38ab-403f-b1ab-3da254e90305 [INVITE] Request payload: { email: '***@***.com', role: 'member', teamId: 'none', resend: false, organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305' } [INVITE] Validating inviter permissions for user: ffyszqeZqE6tJHyiVabJyS22p9PK8nSu in organization: b0e914e2-38ab-403f-b1ab-3da254e90305 [INVITE] Inviter validation passed: { userId: 'ffyszqeZqE6tJHyiVabJyS22p9PK8nSu', organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305', role: 'owner' } [INVITE] Calling BetterAuth invite-member endpoint: { url: 'http://localhost:3000/api/auth/organization/invite-member', payload: { email: '***@***.com', role: 'member', organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305' }, cookieHeaderLength: 304, hasSessionCookie: true } [INVITE] BetterAuth response received: { status: 500, statusText: 'Internal Server Error', headers: {...}, organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305', userId: 'ffyszqeZqE6tJHyiVabJyS22p9PK8nSu' } [INVITE] BetterAuth raw response body: [INVITE] Could not parse BetterAuth error response as JSON: { responseText: '', organizationId: 'b0e914e2-38ab-403f-b1ab-3da254e90305', userId: 'ffyszqeZqE6tJHyiVabJyS22p9PK8nSu' } ``` ## Database State Verification We verified that all data is correctly structured: ### Member Record ```json { "id": "37a97061-ebdc-4fdf-8c8d-d98b62d407cf", "role": "owner", "dynamicRoles": null, "userName": "Admin", "orgName": "Test Software" } ``` ### OrganizationRole Record (owner) ```json { "name": "owner", "permissions": "{\"organization\":[\"create\",\"read\",\"update\",\"delete\",\"manage\"],\"member\":[\"create\",\"read\",\"update\",\"delete\",\"invite\"],\"role\":[\"create\",\"read\",\"update\",\"delete\"],\"invitation\":[\"create\",\"read\",\"update\",\"delete\",\"cancel\"],\"sale\":[\"create\",\"read\",\"update\",\"delete\"],\"marketing\":[\"create\",\"read\",\"update\",\"delete\"],\"dashboard\":[\"read\",\"export\",\"manage\"],\"settings\":[\"read\",\"update\"],\"audit\":[\"read\"]}" } ``` **Verification:** - [x] Member record exists with valid role - [x] OrganizationRole record exists for "owner" - [x] Permissions JSON is valid and parseable - [x] Member has `invitation:create` permission - [x] `dynamicRoles` field is `null` (not undefined) ## Workaround **Disabling dynamic access control resolves the issue completely:** ```typescript organization({ ac, roles: { owner, admin: adminRole, member }, dynamicAccessControl: { enabled: false, // ✅ Works perfectly }, // ... rest of config }) ``` With `enabled: false`, invitations are sent successfully without any errors. ## Analysis The error occurs **inside BetterAuth's code** when `dynamicAccessControl` is enabled. Based on our investigation: 1. **The error happens before** the `sendInvitationEmail` hook is called 2. **All database data is valid** - no undefined or malformed JSON 3. **The error is consistent** - happens on every invitation attempt with dynamic AC enabled 4. **Cookies are correctly passed** - session is valid and authenticated 5. **The response body is empty** - BetterAuth returns 500 with no error details ### Hypothesis When `dynamicAccessControl` is enabled, BetterAuth attempts to: 1. Load the inviter's member record 2. Load the inviter's role from the OrganizationRole table 3. Parse the permissions JSON 4. **Somewhere in this process**, it tries to parse a field that is `undefined` The error `"undefined" is not valid JSON` suggests that BetterAuth is calling `JSON.parse()` on a value that is literally the string `"undefined"` or an actual `undefined` value. ## Possible Root Causes 1. **BetterAuth expects a field that doesn't exist** in the Member or OrganizationRole table 2. **BetterAuth tries to parse `dynamicRoles`** when it's `null` and converts it incorrectly 3. **BetterAuth's internal query** returns `undefined` for a field it expects to exist 4. **Serialization issue** where `undefined` is converted to the string `"undefined"` ## Impact - **Severity:** High - Completely blocks invitation functionality when dynamic access control is enabled - **Workaround Available:** Yes - disable dynamic access control - **Data Loss:** No - **Security Impact:** None ## Additional Context - The issue was discovered after implementing all static roles in the OrganizationRole table as required by dynamic access control - The access control configuration matches the database permissions exactly - The issue persists even after server restarts and cache clearing - The issue occurs in both development and production builds ## Requested Fix Please investigate why BetterAuth attempts to parse `undefined` when `dynamicAccessControl` is enabled, and ensure that: 1. All fields are properly checked for `null`/`undefined` before parsing 2. Error messages include more details about which field caused the parse error 3. The response body includes error details even when internal errors occur ## Related Documentation - [BetterAuth Organization Plugin](https://www.better-auth.com/docs/plugins/organization) - [BetterAuth Dynamic Access Control](https://www.better-auth.com/docs/plugins/organization#dynamic-access-control) - [BetterAuth Invitations](https://www.better-auth.com/docs/plugins/organization#invitations) --- **Thank you for your attention to this issue!** We're happy to provide additional information or test fixes if needed.
GiteaMirror added the lockedbug labels 2026-04-15 17:44:30 -05:00
Author
Owner

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

tag @better-auth-agent if you would like an answer from the Agent

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3507279641 --> @better-auth-agent[bot] commented on GitHub (Nov 9, 2025): tag @better-auth-agent if you would like an answer from the Agent <!-- 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 (Nov 9, 2025):

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

This error is caused by BetterAuth calling JSON.parse on a value that is undefined or null when dynamic access control is enabled. In the dynamic access control logic, permissions for roles are loaded from the OrganizationRole table and parsed from the permissions JSON string. If the permissions field is missing or undefined for any role, or if another field like dynamicRoles is undefined, the code attempts to parse it without a null/undefined check, resulting in the JSON.parse(undefined) error you’re seeing.

Recent code and PRs confirm that there are places where JSON.parse is called directly on these fields without explicit checks for null or undefined values, especially in the dynamic access control and invitation logic. This means that if the database or in-memory object has a missing or undefined value, the error will occur. This is a known class of bug in the organization plugin and dynamic access control logic, as seen in related issues with similar symptoms (source).

To prevent this, the code should check that any value passed to JSON.parse is not null or undefined (for example, if (permissionsString != null)), and ideally provide a fallback or throw a more descriptive error if the field is missing. Improving error handling to include which field caused the parse error and returning error details in the response body would also make debugging much easier.

For now, the only workaround is to keep dynamic access control disabled until a fix is released that adds these checks and improves error reporting.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3507284253 --> @dosubot[bot] commented on GitHub (Nov 9, 2025): <!-- Greeting --> Hi @iptoux! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This error is caused by BetterAuth calling `JSON.parse` on a value that is `undefined` or `null` when dynamic access control is enabled. In the dynamic access control logic, permissions for roles are loaded from the OrganizationRole table and parsed from the `permissions` JSON string. If the `permissions` field is missing or undefined for any role, or if another field like `dynamicRoles` is undefined, the code attempts to parse it without a null/undefined check, resulting in the `JSON.parse(undefined)` error you’re seeing. Recent code and PRs confirm that there are places where `JSON.parse` is called directly on these fields without explicit checks for null or undefined values, especially in the dynamic access control and invitation logic. This means that if the database or in-memory object has a missing or undefined value, the error will occur. This is a known class of bug in the organization plugin and dynamic access control logic, as seen in related issues with similar symptoms ([source](https://github.com/better-auth/better-auth/issues/4033)). To prevent this, the code should check that any value passed to `JSON.parse` is not null or undefined (for example, `if (permissionsString != null)`), and ideally provide a fallback or throw a more descriptive error if the field is missing. Improving error handling to include which field caused the parse error and returning error details in the response body would also make debugging much easier. For now, the only workaround is to keep dynamic access control disabled until a fix is released that adds these checks and improves error reporting. <!-- 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/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/e8dcf805-8bdd-4492-aca9-1ad7c981edeb?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/5860)
Author
Owner

@ping-maxwell commented on GitHub (Nov 12, 2025):

Im pretty sure this is happening because you have defined permissions instead of permission in the organization role schema?

<!-- gh-comment-id:3520463911 --> @ping-maxwell commented on GitHub (Nov 12, 2025): Im pretty sure this is happening because you have defined `permissions` instead of `permission` in the organization role schema?
Author
Owner

@joeyorlando commented on GitHub (Nov 17, 2025):

I ran into something slightly similar in that I had tweaked the text output db schema column to be json type (such that I could strongly type it using drizzle's .$type<Permissions>() helper).. turns out that was fun to debug 😄

agree with what @ping-maxwell said, you likely need to just rename permissions to permission

<!-- gh-comment-id:3539704675 --> @joeyorlando commented on GitHub (Nov 17, 2025): I ran into something _slightly_ similar in that I had tweaked the `text` output db schema column to be `json` type (such that I could strongly type it using `drizzle`'s `.$type<Permissions>()` helper).. turns out that was fun to debug 😄 agree with what @ping-maxwell said, you likely need to just rename `permissions` to `permission`
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#18992