{"record":{"id":"b2357f241747af47","repo":"mastra-ai/mastra","slug":"mastraauthbetterauth-needs-a-database-to-build-its","errorCode":null,"errorMessage":"MastraAuthBetterAuth needs a database to build its own better-auth instance, but the host passed none. Use a storage backend that exposes an auth database, or pass your own configured `auth` instance.","messagePattern":"MastraAuthBetterAuth needs a database to build its own better-auth instance, but the host passed none\\. Use a storage backend that exposes an auth database, or pass your own configured `auth` instance\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/better-auth/src/index.ts","lineNumber":280,"sourceCode":"  // IAuthInit implementation\n  // ============================================\n\n  /**\n   * One-time host initialization. In deferred instance mode (no `auth` in the\n   * options) this builds the provider-owned `betterAuth()` instance on the\n   * host's auth database; schema migrations then run lazily behind a\n   * once-per-process latch on first use.\n   *\n   * Bring-your-own `auth` instances skip construction entirely — the caller\n   * owns their database and migrations.\n   */\n  async init(ctx: AuthInitContext): Promise<void> {\n    this.#crossSite = (ctx.allowedOrigins?.length ?? 0) > 0;\n    if (this.#auth) return; // bring-your-own instance: nothing to build\n\n    const authDb = ctx.database as TaggedAuthDatabase | undefined;\n    if (!authDb) {\n      throw new Error(\n        'MastraAuthBetterAuth needs a database to build its own better-auth instance, but the host passed none. ' +\n          'Use a storage backend that exposes an auth database, or pass your own configured `auth` instance.',\n      );\n    }\n    // Map the host's tagged auth-database handle onto better-auth's `database`\n    // option: pg pool directly, libsql via its kysely dialect, anything else\n    // passed through as-is (the host owns its compatibility).\n    const database: BetterAuthOptions['database'] =\n      authDb.dialect === 'postgres'\n        ? (authDb.pool as Extract<BetterAuthOptions['database'], { query: unknown }>)\n        : authDb.dialect === 'libsql'\n          ? {\n              dialect: new LibsqlDialect({ client: authDb.client } as ConstructorParameters<typeof LibsqlDialect>[0]),\n              type: 'sqlite' as const,\n            }\n          : (authDb.database as BetterAuthOptions['database']);\n    const allowedOrigins = ctx.allowedOrigins ?? [];\n    // Widen to BetterAuthOptions before calling betterAuth(): its return type","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/better-auth/src/index.ts#L262-L298","documentation":"In deferred mode (constructed with `secret`, no `auth`), init() builds a Better Auth instance on a database supplied by the host via AuthInitContext.database. If the host passes no database (ctx.database is undefined), Better Auth cannot be constructed, so init() throws. Bring-your-own instances return early and never hit this path.","triggerScenarios":"async init(ctx) runs with this.#auth unset and `ctx.database as TaggedAuthDatabase` undefined — i.e. host storage backend does not expose a tagged auth database while the provider was created with only a secret.","commonSituations":"Using a storage backend that doesn't expose an auth database (e.g. certain in-memory or non-SQL storage adapters); upgrading Mastra core where the host no longer passes ctx.database; forgetting the storage/database configuration entirely in server options.","solutions":["Pass your own configured Better Auth instance so no host database is needed: new MastraAuthBetterAuth({ auth: betterAuth({...}) }).","Configure a storage backend that exposes an auth database (e.g. Postgres/LibSQL backed storage) so ctx.database is populated.","Check that your server storage config is actually applied to the auth provider's init context."],"exampleFix":"// before\nconst auth = new MastraAuthBetterAuth({ secret: process.env.BETTER_AUTH_SECRET });\n// server has no storage -> init() has no database\n\n// after\nconst auth = new MastraAuthBetterAuth({\n  auth: betterAuth({ database: pgPool, secret: process.env.BETTER_AUTH_SECRET }),\n});\n// or configure Postgres storage on the server so init() receives ctx.database","handlingStrategy":"validation","validationCode":"if (!authDb) {\n  throw new Error('Storage backend does not expose an auth database; pass auth: betterAuth({...}) to MastraAuthBetterAuth instead of secret-only mode');\n}\nawait provider.init(ctx);","typeGuard":"function isTaggedAuthDatabase(db: unknown): db is TaggedAuthDatabase {\n  return typeof db === 'object' && db !== null && ('pool' in db || 'url' in db || 'dialect' in db);\n}","tryCatchPattern":"try {\n  await provider.init(ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('needs a database')) {\n    throw new Error('Configure Postgres/LibSQL storage on the server or supply your own betterAuth instance', { cause: e });\n  }\n  throw e;\n}","preventionTips":["Configure a SQL-backed storage (Postgres/LibSQL) on the server when using secret-only deferred mode.","Prefer bring-your-own betterAuth({ database }) mode when your storage backend has no auth database.","Assert the presence of ctx.database in a startup smoke test."],"tags":["configuration","database","better-auth","storage"],"backgroundTag":"missing-database","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}