mastra-ai/mastra · error
MastraAuthBetterAuth needs a database to build its own bette
Error message
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.
What it means
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.
Source
Thrown at auth/better-auth/src/index.ts:280
// IAuthInit implementation
// ============================================
/**
* One-time host initialization. In deferred instance mode (no `auth` in the
* options) this builds the provider-owned `betterAuth()` instance on the
* host's auth database; schema migrations then run lazily behind a
* once-per-process latch on first use.
*
* Bring-your-own `auth` instances skip construction entirely — the caller
* owns their database and migrations.
*/
async init(ctx: AuthInitContext): Promise<void> {
this.#crossSite = (ctx.allowedOrigins?.length ?? 0) > 0;
if (this.#auth) return; // bring-your-own instance: nothing to build
const authDb = ctx.database as TaggedAuthDatabase | undefined;
if (!authDb) {
throw new Error(
'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.',
);
}
// Map the host's tagged auth-database handle onto better-auth's `database`
// option: pg pool directly, libsql via its kysely dialect, anything else
// passed through as-is (the host owns its compatibility).
const database: BetterAuthOptions['database'] =
authDb.dialect === 'postgres'
? (authDb.pool as Extract<BetterAuthOptions['database'], { query: unknown }>)
: authDb.dialect === 'libsql'
? {
dialect: new LibsqlDialect({ client: authDb.client } as ConstructorParameters<typeof LibsqlDialect>[0]),
type: 'sqlite' as const,
}
: (authDb.database as BetterAuthOptions['database']);
const allowedOrigins = ctx.allowedOrigins ?? [];
// Widen to BetterAuthOptions before calling betterAuth(): its return typeView on GitHub (pinned to 75dd419e61)
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.
Example fix
// before
const auth = new MastraAuthBetterAuth({ secret: process.env.BETTER_AUTH_SECRET });
// server has no storage -> init() has no database
// after
const auth = new MastraAuthBetterAuth({
auth: betterAuth({ database: pgPool, secret: process.env.BETTER_AUTH_SECRET }),
});
// or configure Postgres storage on the server so init() receives ctx.database Defensive patterns
Strategy: validation
Validate before calling
if (!authDb) {
throw new Error('Storage backend does not expose an auth database; pass auth: betterAuth({...}) to MastraAuthBetterAuth instead of secret-only mode');
}
await provider.init(ctx); Type guard
function isTaggedAuthDatabase(db: unknown): db is TaggedAuthDatabase {
return typeof db === 'object' && db !== null && ('pool' in db || 'url' in db || 'dialect' in db);
} Try / catch
try {
await provider.init(ctx);
} catch (e) {
if (e instanceof Error && e.message.includes('needs a database')) {
throw new Error('Configure Postgres/LibSQL storage on the server or supply your own betterAuth instance', { cause: e });
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- AcpAgent does not support resuming suspended generate calls
- ACP prompt stopped before completing: ${response.stopReason}
- Better Auth instance is required. Please provide the auth op
- [Slack] Failed to resolve Mastra storage. Ensure your Mastra
- [Slack] No storage available. SlackProvider requires persist
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/b2357f241747af47.
Report an issue: GitHub.