[GH-ISSUE #8893] Redis Secondary Storage drops session from schema even though oauth-provider needs it #28547

Closed
opened 2026-04-17 19:59:58 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @jaybyrrd on GitHub (Apr 1, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8893

Originally assigned to: @ping-maxwell on GitHub.

Is this suited for github?

  • Yes, this is suited for github

Reproduction

Bug: secondaryStorage with redisStorage removes session and verification models from adapter schema

Package: @better-auth/redis-storage@1.5.6
Better Auth: 1.5.6

Description:
When using redisStorage as secondaryStorage, the session and verification models are removed from the internal adapter schema. This causes any plugin that queries sessions via ctx.context.adapter.findOne({ model: "session" }) to throw:

BetterAuthError: Model "session" not found in schema

**Reproduction:**

```ts
import { betterAuth } from "better-auth";
import { jwt, bearer, openAPI, organization } from "better-auth/plugins";
import { oauthProvider } from "@better-auth/oauth-provider";
import { redisStorage } from "@better-auth/redis-storage";
import Redis from "ioredis";

const auth = betterAuth({
  secret: "...",
  database: new Pool({ connectionString: "..." }),
  secondaryStorage: redisStorage({
    client: new Redis({ host: "localhost", port: 6379 }),
  }),
  emailAndPassword: { enabled: true },
  plugins: [
    jwt({ disableSettingJwtHeader: true }),
    bearer(),
    openAPI(),
    organization(),
    oauthProvider({ loginPage: "/sign-in", consentPage: "/consent" }),
  ],
});

const ctx = await auth.$context;
console.log(Object.keys((ctx as any).tables));
// Missing: "session", "verification"

await ctx.adapter.findOne({ model: "session", where: [{ field: "id", value: "test" }] });
// Throws: BetterAuthError: Model "session" not found in schema

Current vs. Expected behavior

Expected: session and verification remain in the adapter schema when secondaryStorage is configured.

Actual: Both models are removed, breaking any plugin that queries them via the adapter.

What version of Better Auth are you using?

1.5.4

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 23.6.0: Fri Jul  5 17:56:15 PDT 2024; root:xnu-10063.141.1~2/RELEASE_ARM64_T6031",
    "release": "23.6.0",
    "cpuCount": 14,
    "cpuModel": "Apple M3 Max",
    "totalMemory": "96.00 GB",
    "freeMemory": "0.67 GB"
  },
  "node": {
    "version": "v20.17.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.8.2"
  },
  "frameworks": [
    {
      "name": "hono",
      "version": "^4.7.6"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.19.0"
    }
  ],
  "betterAuth": {
    "version": "^1.5.6",
    "config": {
      "secret": "[REDACTED]",
      "secondaryStorage": {},
      "disabledPaths": [
        "/token"
      ],
      "trustedOrigins": [
        "*"
      ],
      "database": {
        "_events": {},
        "_eventsCount": 0,
        "options": {
          "connectionString": "[REDACTED]",
          "ssl": false,
          "max": 10,
          "min": 0,
          "maxUses": null,
          "allowExitOnIdle": false,
          "maxLifetimeSeconds": 0,
          "idleTimeoutMillis": 10000
        },
        "_clients": [],
        "_idle": [],
        "_expired": {},
        "_pendingQueue": [],
        "ending": false,
        "ended": false
      },
      "emailAndPassword": {
        "enabled": true,
        "password": {}
      },
      "plugins": [
        {
          "name": "jwt",
          "config": {
            "id": "jwt",
            "options": {
              "disableSettingJwtHeader": true,
              "jwks": {
                "jwksPath": "/.well-known/jwks.json"
              }
            },
            "endpoints": {},
            "hooks": {
              "after": [
                {}
              ]
            },
            "schema": {
              "jwks": {
                "fields": {
                  "publicKey": {
                    "type": "string",
                    "required": true
                  },
                  "privateKey": {
                    "type": "string",
                    "required": true
                  },
                  "createdAt": {
                    "type": "date",
                    "required": true
                  },
                  "expiresAt": {
                    "type": "date",
                    "required": false
                  }
                }
              }
            }
          }
        },
        {
          "name": "bearer",
          "config": {
            "id": "bearer",
            "hooks": {
              "before": [
                {}
              ],
              "after": [
                {}
              ]
            }
          }
        },
        {
          "name": "open-api",
          "config": {
            "id": "open-api",
            "endpoints": {}
          }
        },
        {
          "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
                  }
                }
              },
              "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_ORGANIZATION": {
                "code": "USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION",
                "message": "User is already invited to this organization"
              },
              "INVITATION_NOT_FOUND": {
                "code": "INVITATION_NOT_FOUND",
                "message": "Invitation not found"
              },
              "YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION": {
                "code": "YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION",
                "message": "You are not the recipient of the invitation"
              },
              "EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION": {
                "code": "EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION",
                "message": "Email verification required before accepting or rejecting invitation"
              },
              "YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION",
                "message": "You are not allowed to cancel this invitation"
              },
              "INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION": {
                "code": "INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION",
                "message": "Inviter is no longer a member of the organization"
              },
              "YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE",
                "message": "You are not allowed to invite a user with this role"
              },
              "FAILED_TO_RETRIEVE_INVITATION": {
                "code": "FAILED_TO_RETRIEVE_INVITATION",
                "message": "Failed to retrieve invitation"
              },
              "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS": {
                "code": "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS",
                "message": "You have reached the maximum number of teams"
              },
              "UNABLE_TO_REMOVE_LAST_TEAM": {
                "code": "UNABLE_TO_REMOVE_LAST_TEAM",
                "message": "Unable to remove last team"
              },
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER",
                "message": "You are not allowed to update this member"
              },
              "ORGANIZATION_MEMBERSHIP_LIMIT_REACHED": {
                "code": "ORGANIZATION_MEMBERSHIP_LIMIT_REACHED",
                "message": "Organization membership limit reached"
              },
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION",
                "message": "You are not allowed to create teams in this organization"
              },
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION",
                "message": "You are not allowed to delete teams in this organization"
              },
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM",
                "message": "You are not allowed to update this team"
              },
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM",
                "message": "You are not allowed to delete this team"
              },
              "INVITATION_LIMIT_REACHED": {
                "code": "INVITATION_LIMIT_REACHED",
                "message": "Invitation limit reached"
              },
              "TEAM_MEMBER_LIMIT_REACHED": {
                "code": "TEAM_MEMBER_LIMIT_REACHED",
                "message": "Team member limit reached"
              },
              "USER_IS_NOT_A_MEMBER_OF_THE_TEAM": {
                "code": "USER_IS_NOT_A_MEMBER_OF_THE_TEAM",
                "message": "User is not a member of the team"
              },
              "YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM": {
                "code": "YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM",
                "message": "You are not allowed to list the members of this team"
              },
              "YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM": {
                "code": "YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM",
                "message": "You do not have an active team"
              },
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER",
                "message": "You are not allowed to create a new member"
              },
              "YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER",
                "message": "You are not allowed to remove a team member"
              },
              "YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION",
                "message": "You are not allowed to access this organization as an owner"
              },
              "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION": {
                "code": "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION",
                "message": "You are not a member of this organization"
              },
              "MISSING_AC_INSTANCE": {
                "code": "MISSING_AC_INSTANCE",
                "message": "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": {
                "code": "YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE",
                "message": "You must be in an organization to create a role"
              },
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE",
                "message": "You are not allowed to create a role"
              },
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE",
                "message": "You are not allowed to update a role"
              },
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE",
                "message": "You are not allowed to delete a role"
              },
              "YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE",
                "message": "You are not allowed to read a role"
              },
              "YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE",
                "message": "You are not allowed to list a role"
              },
              "YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE": {
                "code": "YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE",
                "message": "You are not allowed to get a role"
              },
              "TOO_MANY_ROLES": {
                "code": "TOO_MANY_ROLES",
                "message": "This organization has too many roles"
              },
              "INVALID_RESOURCE": {
                "code": "INVALID_RESOURCE",
                "message": "The provided permission includes an invalid resource"
              },
              "ROLE_NAME_IS_ALREADY_TAKEN": {
                "code": "ROLE_NAME_IS_ALREADY_TAKEN",
                "message": "That role name is already taken"
              },
              "CANNOT_DELETE_A_PRE_DEFINED_ROLE": {
                "code": "CANNOT_DELETE_A_PRE_DEFINED_ROLE",
                "message": "Cannot delete a pre-defined role"
              },
              "ROLE_IS_ASSIGNED_TO_MEMBERS": {
                "code": "ROLE_IS_ASSIGNED_TO_MEMBERS",
                "message": "Cannot delete a role that is assigned to members. Please reassign the members to a different role first"
              }
            },
            "options": {
              "allowUserToCreateOrganization": true,
              "organizationLimit": 5,
              "membershipLimit": 1000
            }
          }
        },
        {
          "name": "oauth-provider",
          "config": {
            "id": "oauth-provider",
            "options": {
              "codeExpiresIn": 600,
              "accessTokenExpiresIn": 3600,
              "m2mAccessTokenExpiresIn": 3600,
              "refreshTokenExpiresIn": 2592000,
              "allowUnauthenticatedClientRegistration": false,
              "allowDynamicClientRegistration": false,
              "disableJwtPlugin": false,
              "storeClientSecret": "[REDACTED]",
              "storeTokens": "hashed",
              "grantTypes": [
                "authorization_code",
                "client_credentials",
                "refresh_token"
              ],
              "loginPage": "/auth/sign-in",
              "consentPage": "/consent",
              "prefix": {
                "opaqueAccessToken": "[REDACTED]",
                "refreshToken": "[REDACTED]",
                "clientSecret": "[REDACTED]"
              },
              "silenceWarnings": {
                "oauthAuthServerConfig": true
              },
              "scopes": [
                "openid",
                "profile",
                "email",
                "offline_access"
              ],
              "claims": [
                "sub",
                "iss",
                "aud",
                "exp",
                "iat",
                "sid",
                "scope",
                "azp",
                "email",
                "email_verified",
                "name",
                "picture",
                "family_name",
                "given_name"
              ]
            },
            "hooks": {
              "before": [
                {}
              ],
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "schema": {
              "oauthClient": {
                "modelName": "oauthClient",
                "fields": {
                  "clientId": {
                    "type": "string",
                    "unique": true,
                    "required": true
                  },
                  "clientSecret": {
                    "type": "string",
                    "required": false
                  },
                  "disabled": {
                    "type": "boolean",
                    "defaultValue": false,
                    "required": false
                  },
                  "skipConsent": {
                    "type": "boolean",
                    "required": false
                  },
                  "enableEndSession": {
                    "type": "boolean",
                    "required": false
                  },
                  "subjectType": {
                    "type": "string",
                    "required": false
                  },
                  "scopes": {
                    "type": "string[]",
                    "required": false
                  },
                  "userId": {
                    "type": "string",
                    "required": false,
                    "references": {
                      "model": "user",
                      "field": "id"
                    }
                  },
                  "createdAt": {
                    "type": "date",
                    "required": false
                  },
                  "updatedAt": {
                    "type": "date",
                    "required": false
                  },
                  "name": {
                    "type": "string",
                    "required": false
                  },
                  "uri": {
                    "type": "string",
                    "required": false
                  },
                  "icon": {
                    "type": "string",
                    "required": false
                  },
                  "contacts": {
                    "type": "string[]",
                    "required": false
                  },
                  "tos": {
                    "type": "string",
                    "required": false
                  },
                  "policy": {
                    "type": "string",
                    "required": false
                  },
                  "softwareId": {
                    "type": "string",
                    "required": false
                  },
                  "softwareVersion": {
                    "type": "string",
                    "required": false
                  },
                  "softwareStatement": {
                    "type": "string",
                    "required": false
                  },
                  "redirectUris": {
                    "type": "string[]",
                    "required": true
                  },
                  "postLogoutRedirectUris": {
                    "type": "string[]",
                    "required": false
                  },
                  "tokenEndpointAuthMethod": {
                    "type": "string",
                    "required": false
                  },
                  "grantTypes": {
                    "type": "string[]",
                    "required": false
                  },
                  "responseTypes": {
                    "type": "string[]",
                    "required": false
                  },
                  "public": {
                    "type": "boolean",
                    "required": false
                  },
                  "type": {
                    "type": "string",
                    "required": false
                  },
                  "requirePKCE": {
                    "type": "boolean",
                    "required": false
                  },
                  "referenceId": {
                    "type": "string",
                    "required": false
                  },
                  "metadata": {
                    "type": "json",
                    "required": false
                  }
                }
              },
              "oauthRefreshToken": {
                "fields": {
                  "token": {
                    "type": "string",
                    "required": true
                  },
                  "clientId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "oauthClient",
                      "field": "clientId"
                    }
                  },
                  "sessionId": {
                    "type": "string",
                    "required": false,
                    "references": {
                      "model": "session",
                      "field": "id",
                      "onDelete": "set null"
                    }
                  },
                  "userId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "user",
                      "field": "id"
                    }
                  },
                  "referenceId": {
                    "type": "string",
                    "required": false
                  },
                  "expiresAt": {
                    "type": "date"
                  },
                  "createdAt": {
                    "type": "date"
                  },
                  "revoked": {
                    "type": "date",
                    "required": false
                  },
                  "authTime": {
                    "type": "date",
                    "required": false
                  },
                  "scopes": {
                    "type": "string[]",
                    "required": true
                  }
                }
              },
              "oauthAccessToken": {
                "modelName": "oauthAccessToken",
                "fields": {
                  "token": {
                    "type": "string",
                    "unique": true
                  },
                  "clientId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "oauthClient",
                      "field": "clientId"
                    }
                  },
                  "sessionId": {
                    "type": "string",
                    "required": false,
                    "references": {
                      "model": "session",
                      "field": "id",
                      "onDelete": "set null"
                    }
                  },
                  "userId": {
                    "type": "string",
                    "required": false,
                    "references": {
                      "model": "user",
                      "field": "id"
                    }
                  },
                  "referenceId": {
                    "type": "string",
                    "required": false
                  },
                  "refreshId": {
                    "type": "string",
                    "required": false,
                    "references": {
                      "model": "oauthRefreshToken",
                      "field": "id"
                    }
                  },
                  "expiresAt": {
                    "type": "date"
                  },
                  "createdAt": {
                    "type": "date"
                  },
                  "scopes": {
                    "type": "string[]",
                    "required": true
                  }
                }
              },
              "oauthConsent": {
                "modelName": "oauthConsent",
                "fields": {
                  "clientId": {
                    "type": "string",
                    "required": true,
                    "references": {
                      "model": "oauthClient",
                      "field": "clientId"
                    }
                  },
                  "userId": {
                    "type": "string",
                    "required": false,
                    "references": {
                      "model": "user",
                      "field": "id"
                    }
                  },
                  "referenceId": {
                    "type": "string",
                    "required": false
                  },
                  "scopes": {
                    "type": "string[]",
                    "required": true
                  },
                  "createdAt": {
                    "type": "date"
                  },
                  "updatedAt": {
                    "type": "date"
                  }
                }
              }
            },
            "rateLimit": [
              {
                "window": 60,
                "max": 20
              },
              {
                "window": 60,
                "max": 30
              },
              {
                "window": 60,
                "max": 100
              },
              {
                "window": 60,
                "max": 30
              },
              {
                "window": 60,
                "max": 5
              },
              {
                "window": 60,
                "max": 60
              }
            ]
          }
        }
      ]
    }
  }
}

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

Backend

Auth config (if applicable)


Additional context

No response

Originally created by @jaybyrrd on GitHub (Apr 1, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8893 Originally assigned to: @ping-maxwell on GitHub. ### Is this suited for github? - [ ] Yes, this is suited for github ### Reproduction **Bug: `secondaryStorage` with `redisStorage` removes `session` and `verification` models from adapter schema** **Package:** `@better-auth/redis-storage@1.5.6` **Better Auth:** `1.5.6` **Description:** When using `redisStorage` as `secondaryStorage`, the `session` and `verification` models are removed from the internal adapter schema. This causes any plugin that queries sessions via `ctx.context.adapter.findOne({ model: "session" })` to throw: ``` BetterAuthError: Model "session" not found in schema **Reproduction:** ```ts import { betterAuth } from "better-auth"; import { jwt, bearer, openAPI, organization } from "better-auth/plugins"; import { oauthProvider } from "@better-auth/oauth-provider"; import { redisStorage } from "@better-auth/redis-storage"; import Redis from "ioredis"; const auth = betterAuth({ secret: "...", database: new Pool({ connectionString: "..." }), secondaryStorage: redisStorage({ client: new Redis({ host: "localhost", port: 6379 }), }), emailAndPassword: { enabled: true }, plugins: [ jwt({ disableSettingJwtHeader: true }), bearer(), openAPI(), organization(), oauthProvider({ loginPage: "/sign-in", consentPage: "/consent" }), ], }); const ctx = await auth.$context; console.log(Object.keys((ctx as any).tables)); // Missing: "session", "verification" await ctx.adapter.findOne({ model: "session", where: [{ field: "id", value: "test" }] }); // Throws: BetterAuthError: Model "session" not found in schema ``` ### Current vs. Expected behavior **Expected:** `session` and `verification` remain in the adapter schema when `secondaryStorage` is configured. **Actual:** Both models are removed, breaking any plugin that queries them via the adapter. ### What version of Better Auth are you using? 1.5.4 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 23.6.0: Fri Jul 5 17:56:15 PDT 2024; root:xnu-10063.141.1~2/RELEASE_ARM64_T6031", "release": "23.6.0", "cpuCount": 14, "cpuModel": "Apple M3 Max", "totalMemory": "96.00 GB", "freeMemory": "0.67 GB" }, "node": { "version": "v20.17.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.8.2" }, "frameworks": [ { "name": "hono", "version": "^4.7.6" } ], "databases": [ { "name": "pg", "version": "^8.19.0" } ], "betterAuth": { "version": "^1.5.6", "config": { "secret": "[REDACTED]", "secondaryStorage": {}, "disabledPaths": [ "/token" ], "trustedOrigins": [ "*" ], "database": { "_events": {}, "_eventsCount": 0, "options": { "connectionString": "[REDACTED]", "ssl": false, "max": 10, "min": 0, "maxUses": null, "allowExitOnIdle": false, "maxLifetimeSeconds": 0, "idleTimeoutMillis": 10000 }, "_clients": [], "_idle": [], "_expired": {}, "_pendingQueue": [], "ending": false, "ended": false }, "emailAndPassword": { "enabled": true, "password": {} }, "plugins": [ { "name": "jwt", "config": { "id": "jwt", "options": { "disableSettingJwtHeader": true, "jwks": { "jwksPath": "/.well-known/jwks.json" } }, "endpoints": {}, "hooks": { "after": [ {} ] }, "schema": { "jwks": { "fields": { "publicKey": { "type": "string", "required": true }, "privateKey": { "type": "string", "required": true }, "createdAt": { "type": "date", "required": true }, "expiresAt": { "type": "date", "required": false } } } } } }, { "name": "bearer", "config": { "id": "bearer", "hooks": { "before": [ {} ], "after": [ {} ] } } }, { "name": "open-api", "config": { "id": "open-api", "endpoints": {} } }, { "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 } } }, "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_ORGANIZATION": { "code": "USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION", "message": "User is already invited to this organization" }, "INVITATION_NOT_FOUND": { "code": "INVITATION_NOT_FOUND", "message": "Invitation not found" }, "YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION": { "code": "YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION", "message": "You are not the recipient of the invitation" }, "EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION": { "code": "EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION", "message": "Email verification required before accepting or rejecting invitation" }, "YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION", "message": "You are not allowed to cancel this invitation" }, "INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION": { "code": "INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION", "message": "Inviter is no longer a member of the organization" }, "YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE", "message": "You are not allowed to invite a user with this role" }, "FAILED_TO_RETRIEVE_INVITATION": { "code": "FAILED_TO_RETRIEVE_INVITATION", "message": "Failed to retrieve invitation" }, "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS": { "code": "YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS", "message": "You have reached the maximum number of teams" }, "UNABLE_TO_REMOVE_LAST_TEAM": { "code": "UNABLE_TO_REMOVE_LAST_TEAM", "message": "Unable to remove last team" }, "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER": { "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER", "message": "You are not allowed to update this member" }, "ORGANIZATION_MEMBERSHIP_LIMIT_REACHED": { "code": "ORGANIZATION_MEMBERSHIP_LIMIT_REACHED", "message": "Organization membership limit reached" }, "YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION", "message": "You are not allowed to create teams in this organization" }, "YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION", "message": "You are not allowed to delete teams in this organization" }, "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM": { "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM", "message": "You are not allowed to update this team" }, "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM": { "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM", "message": "You are not allowed to delete this team" }, "INVITATION_LIMIT_REACHED": { "code": "INVITATION_LIMIT_REACHED", "message": "Invitation limit reached" }, "TEAM_MEMBER_LIMIT_REACHED": { "code": "TEAM_MEMBER_LIMIT_REACHED", "message": "Team member limit reached" }, "USER_IS_NOT_A_MEMBER_OF_THE_TEAM": { "code": "USER_IS_NOT_A_MEMBER_OF_THE_TEAM", "message": "User is not a member of the team" }, "YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM": { "code": "YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM", "message": "You are not allowed to list the members of this team" }, "YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM": { "code": "YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM", "message": "You do not have an active team" }, "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER": { "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER", "message": "You are not allowed to create a new member" }, "YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER": { "code": "YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER", "message": "You are not allowed to remove a team member" }, "YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION", "message": "You are not allowed to access this organization as an owner" }, "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION": { "code": "YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION", "message": "You are not a member of this organization" }, "MISSING_AC_INSTANCE": { "code": "MISSING_AC_INSTANCE", "message": "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": { "code": "YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE", "message": "You must be in an organization to create a role" }, "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE", "message": "You are not allowed to create a role" }, "YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE", "message": "You are not allowed to update a role" }, "YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE", "message": "You are not allowed to delete a role" }, "YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE", "message": "You are not allowed to read a role" }, "YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE", "message": "You are not allowed to list a role" }, "YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE": { "code": "YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE", "message": "You are not allowed to get a role" }, "TOO_MANY_ROLES": { "code": "TOO_MANY_ROLES", "message": "This organization has too many roles" }, "INVALID_RESOURCE": { "code": "INVALID_RESOURCE", "message": "The provided permission includes an invalid resource" }, "ROLE_NAME_IS_ALREADY_TAKEN": { "code": "ROLE_NAME_IS_ALREADY_TAKEN", "message": "That role name is already taken" }, "CANNOT_DELETE_A_PRE_DEFINED_ROLE": { "code": "CANNOT_DELETE_A_PRE_DEFINED_ROLE", "message": "Cannot delete a pre-defined role" }, "ROLE_IS_ASSIGNED_TO_MEMBERS": { "code": "ROLE_IS_ASSIGNED_TO_MEMBERS", "message": "Cannot delete a role that is assigned to members. Please reassign the members to a different role first" } }, "options": { "allowUserToCreateOrganization": true, "organizationLimit": 5, "membershipLimit": 1000 } } }, { "name": "oauth-provider", "config": { "id": "oauth-provider", "options": { "codeExpiresIn": 600, "accessTokenExpiresIn": 3600, "m2mAccessTokenExpiresIn": 3600, "refreshTokenExpiresIn": 2592000, "allowUnauthenticatedClientRegistration": false, "allowDynamicClientRegistration": false, "disableJwtPlugin": false, "storeClientSecret": "[REDACTED]", "storeTokens": "hashed", "grantTypes": [ "authorization_code", "client_credentials", "refresh_token" ], "loginPage": "/auth/sign-in", "consentPage": "/consent", "prefix": { "opaqueAccessToken": "[REDACTED]", "refreshToken": "[REDACTED]", "clientSecret": "[REDACTED]" }, "silenceWarnings": { "oauthAuthServerConfig": true }, "scopes": [ "openid", "profile", "email", "offline_access" ], "claims": [ "sub", "iss", "aud", "exp", "iat", "sid", "scope", "azp", "email", "email_verified", "name", "picture", "family_name", "given_name" ] }, "hooks": { "before": [ {} ], "after": [ {} ] }, "endpoints": {}, "schema": { "oauthClient": { "modelName": "oauthClient", "fields": { "clientId": { "type": "string", "unique": true, "required": true }, "clientSecret": { "type": "string", "required": false }, "disabled": { "type": "boolean", "defaultValue": false, "required": false }, "skipConsent": { "type": "boolean", "required": false }, "enableEndSession": { "type": "boolean", "required": false }, "subjectType": { "type": "string", "required": false }, "scopes": { "type": "string[]", "required": false }, "userId": { "type": "string", "required": false, "references": { "model": "user", "field": "id" } }, "createdAt": { "type": "date", "required": false }, "updatedAt": { "type": "date", "required": false }, "name": { "type": "string", "required": false }, "uri": { "type": "string", "required": false }, "icon": { "type": "string", "required": false }, "contacts": { "type": "string[]", "required": false }, "tos": { "type": "string", "required": false }, "policy": { "type": "string", "required": false }, "softwareId": { "type": "string", "required": false }, "softwareVersion": { "type": "string", "required": false }, "softwareStatement": { "type": "string", "required": false }, "redirectUris": { "type": "string[]", "required": true }, "postLogoutRedirectUris": { "type": "string[]", "required": false }, "tokenEndpointAuthMethod": { "type": "string", "required": false }, "grantTypes": { "type": "string[]", "required": false }, "responseTypes": { "type": "string[]", "required": false }, "public": { "type": "boolean", "required": false }, "type": { "type": "string", "required": false }, "requirePKCE": { "type": "boolean", "required": false }, "referenceId": { "type": "string", "required": false }, "metadata": { "type": "json", "required": false } } }, "oauthRefreshToken": { "fields": { "token": { "type": "string", "required": true }, "clientId": { "type": "string", "required": true, "references": { "model": "oauthClient", "field": "clientId" } }, "sessionId": { "type": "string", "required": false, "references": { "model": "session", "field": "id", "onDelete": "set null" } }, "userId": { "type": "string", "required": true, "references": { "model": "user", "field": "id" } }, "referenceId": { "type": "string", "required": false }, "expiresAt": { "type": "date" }, "createdAt": { "type": "date" }, "revoked": { "type": "date", "required": false }, "authTime": { "type": "date", "required": false }, "scopes": { "type": "string[]", "required": true } } }, "oauthAccessToken": { "modelName": "oauthAccessToken", "fields": { "token": { "type": "string", "unique": true }, "clientId": { "type": "string", "required": true, "references": { "model": "oauthClient", "field": "clientId" } }, "sessionId": { "type": "string", "required": false, "references": { "model": "session", "field": "id", "onDelete": "set null" } }, "userId": { "type": "string", "required": false, "references": { "model": "user", "field": "id" } }, "referenceId": { "type": "string", "required": false }, "refreshId": { "type": "string", "required": false, "references": { "model": "oauthRefreshToken", "field": "id" } }, "expiresAt": { "type": "date" }, "createdAt": { "type": "date" }, "scopes": { "type": "string[]", "required": true } } }, "oauthConsent": { "modelName": "oauthConsent", "fields": { "clientId": { "type": "string", "required": true, "references": { "model": "oauthClient", "field": "clientId" } }, "userId": { "type": "string", "required": false, "references": { "model": "user", "field": "id" } }, "referenceId": { "type": "string", "required": false }, "scopes": { "type": "string[]", "required": true }, "createdAt": { "type": "date" }, "updatedAt": { "type": "date" } } } }, "rateLimit": [ { "window": 60, "max": 20 }, { "window": 60, "max": 30 }, { "window": 60, "max": 100 }, { "window": 60, "max": 30 }, { "window": 60, "max": 5 }, { "window": 60, "max": 60 } ] } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-04-17 19:59:58 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Apr 1, 2026):

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

This is a documented interaction between secondaryStorage and the oauth-provider plugin. When secondaryStorage is configured, better-auth conditionally excludes the session and verification tables from the adapter schema in getAuthTables() [1] [2], but the oauth-provider plugin still needs to query sessions via ctx.context.adapter.findOne({ model: "session" }) in endpoints like token exchange [3] and logout [4].

The fix is to explicitly tell better-auth to keep the session table in the database schema by setting session.storeSessionInDatabase: true:

const auth = betterAuth({
  // ...your existing config
  secondaryStorage: redisStorage({
    client: new Redis({ host: "localhost", port: 6379 }),
  }),
  session: {
    storeSessionInDatabase: true, // Required when using oauth-provider with secondaryStorage
  },
});

The oauth-provider plugin actually has an initialization check that should throw a clear error about this requirement — if you're not seeing that error, it may not be triggering in your version.

Similarly, if you use features that depend on the verification model (magic link, email verification, OAuth state), you'd also need:

verification: {
  storeInDatabase: true,
}

With these flags enabled, data gets written to both Redis (for fast reads with TTL) and the database (for plugins that query through the adapter) [5]. There have been previous reports of this same issue [6] [7], and there's been a broader effort [8] to ensure models always exist in the schema regardless of storage configuration.

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

<!-- gh-comment-id:4171987362 --> @dosubot[bot] commented on GitHub (Apr 1, 2026): <!-- Greeting --> Hi @jaybyrrd! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a documented interaction between `secondaryStorage` and the `oauth-provider` plugin. When `secondaryStorage` is configured, better-auth conditionally **excludes** the `session` and `verification` tables from the adapter schema in `getAuthTables()` [[1]](https://github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/core/src/db/get-tables.ts#L200-L203) [[2]](https://github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/core/src/db/get-tables.ts#L290-L292), but the `oauth-provider` plugin still needs to query sessions via `ctx.context.adapter.findOne({ model: "session" })` in endpoints like token exchange [[3]](https://github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/oauth-provider/src/token.ts#L746-L754) and logout [[4]](https://github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/oauth-provider/src/logout.ts#L163-L166). The **fix** is to explicitly tell better-auth to keep the session table in the database schema by setting `session.storeSessionInDatabase: true`: ```ts const auth = betterAuth({ // ...your existing config secondaryStorage: redisStorage({ client: new Redis({ host: "localhost", port: 6379 }), }), session: { storeSessionInDatabase: true, // Required when using oauth-provider with secondaryStorage }, }); ``` The `oauth-provider` plugin actually has an [initialization check](https://github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/oauth-provider/src/oauth.ts#L171-L176) that should throw a clear error about this requirement — if you're not seeing that error, it may not be triggering in your version. Similarly, if you use features that depend on the `verification` model (magic link, email verification, OAuth state), you'd also need: ```ts verification: { storeInDatabase: true, } ``` With these flags enabled, data gets written to **both** Redis (for fast reads with TTL) and the database (for plugins that query through the adapter) [[5]](https://github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/better-auth/src/db/internal-adapter.ts#L331-L401). There have been previous reports of this same issue [[6]](https://github.com/better-auth/better-auth/issues/6995) [[7]](https://github.com/better-auth/better-auth/issues/8348), and there's been a broader effort [[8]](https://github.com/better-auth/better-auth/pull/8353) to ensure models always exist in the schema regardless of storage configuration. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=0640da43-56f3-4119-a21b-8500d2931ba9&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&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20documented%20interaction%20between%20%60secondaryStorage%60%20and%20the%20%60oauth-provider%60%20plugin.%20When%20%60secondaryStorage%60%20is%20configured%2C%20better-auth%20conditionally%20%2A%2Aexcludes%2A%2A%20the%20%60session%60%20and%20%60verification%60%20tables%20from%20the%20adapter%20schema%20in%20%60getAuthTables%28%29%60%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/core/src/db/get-tables.ts%23L200-L203%29%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/core/src/db/get-tables.ts%23L290-L292%29%2C%20but%20the%20%60oauth-provider%60%20plugin%20still%20needs%20to%20query%20sessions%20via%20%60ctx.context.adapter.findOne%28%7B%20model%3A%20%22session%22%20%7D%29%60%20in%20endpoints%20like%20token%20exchange%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/oauth-provider/src/token.ts%23L746-L754%29%20and%20logout%20%5B%5B4%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/oauth-provider/src/logout.ts%23L163-L166%29.%0A%0AThe%20%2A%2Afix%2A%2A%20is%20to%20explicitly%20tell%20better-auth%20to%20keep%20the%20session%20table%20in%20the%20database%20schema%20by%20setting%20%60session.storeSessionInDatabase%3A%20true%60%3A%0A%0A%60%60%60ts%0Aconst%20auth%20%3D%20betterAuth%28%7B%0A%20%20//%20...your%20existing%20config%0A%20%20secondaryStorage%3A%20redisStorage%28%7B%0A%20%20%20%20client%3A%20new%20Redis%28%7B%20host%3A%20%22localhost%22%2C%20port%3A%206379%20%7D%29%2C%0A%20%20%7D%29%2C%0A%20%20session%3A%20%7B%0A%20%20%20%20storeSessionInDatabase%3A%20true%2C%20//%20Required%20when%20using%20oauth-provider%20with%20secondaryStorage%0A%20%20%7D%2C%0A%7D%29%3B%0A%60%60%60%0A%0AThe%20%60oauth-provider%60%20plugin%20actually%20has%20an%20%5Binitialization%20check%5D%28https%3A//github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/oauth-provider/src/oauth.ts%23L171-L176%29%20that%20should%20throw%20a%20clear%20error%20about%20this%20requirement%20%E2%80%94%20if%20you%27re%20not%20seeing%20that%20error%2C%20it%20may%20not%20be%20triggering%20in%20your%20version.%0A%0ASimilarly%2C%20if%20you%20use%20features%20that%20depend%20on%20the%20%60verification%60%20model%20%28magic%20link%2C%20email%20verification%2C%20OAuth%20state%29%2C%20you%27d%20also%20need%3A%0A%0A%60%60%60ts%0Averification%3A%20%7B%0A%20%20storeInDatabase%3A%20true%2C%0A%7D%0A%60%60%60%0A%0AWith%20these%20flags%20enabled%2C%20data%20gets%20written%20to%20%2A%2Aboth%2A%2A%20Redis%20%28for%20fast%20reads%20with%20TTL%29%20and%20the%20database%20%28for%20plugins%20that%20query%20through%20the%20adapter%29%20%5B%5B5%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/f676436c2c3bb6ecf740949a2ce823cabf6b628c/packages/better-auth/src/db/internal-adapter.ts%23L331-L401%29.%20There%20have%20been%20previous%20reports%20of%20this%20same%20issue%20%5B%5B6%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/6995%29%20%5B%5B7%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/8348%29%2C%20and%20there%27s%20been%20a%20broader%20effort%20%5B%5B8%5D%5D%28https%3A//github.com/better-auth/better-auth/pull/8353%29%20to%20ensure%20models%20always%20exist%20in%20the%20schema%20regardless%20of%20storage%20configuration.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&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/8893)
Author
Owner

@jaybyrrd commented on GitHub (Apr 1, 2026):

Want to confirm that this did fix it, but the check didn't throw a clear error as my database had the table, but my config didn't have the storeInDatabase flags.

<!-- gh-comment-id:4172270950 --> @jaybyrrd commented on GitHub (Apr 1, 2026): Want to confirm that this did fix it, but the check didn't throw a clear error as my database had the table, but my config didn't have the storeInDatabase flags.
Author
Owner

@GautamBytes commented on GitHub (Apr 1, 2026):

Want to confirm that this did fix it, but the check didn't throw a clear error as my database had the table, but my config didn't have the storeInDatabase flags.

The check is config-based, not based on whether the physical table already exists, so it should now fail fast whenever secondaryStorage is enabled and session.storeSessionInDatabase !== true, even if the session table is already present in the database.

<!-- gh-comment-id:4172413614 --> @GautamBytes commented on GitHub (Apr 1, 2026): > Want to confirm that this did fix it, but the check didn't throw a clear error as my database had the table, but my config didn't have the storeInDatabase flags. The check is config-based, not based on whether the physical table already exists, so it should now fail fast whenever `secondaryStorage` is enabled and `session.storeSessionInDatabase !== true`, even if the session table is already present in the database.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28547