mastra-ai/mastra · error
storageBackend is required when injecting a custom storage i
Error message
storageBackend is required when injecting a custom storage instance.
What it means
resolveInjectedStorageBackend() maps an injected MastraCompositeStore to a concrete backend name ('libsql' or 'pg'). It trusts an explicit configuredBackend; otherwise it detects LibSQLStore or PostgresStore by instanceof or ancestor class name. Any other custom storage class is unsupported, so callers must declare the backend themselves via the storageBackend option.
Source
Thrown at mastracode/sdk/src/index.ts:401
return typeof candidate.init === 'function' && typeof candidate.__registerMastra === 'function';
}
/** Cross-copy-safe class check: walks the prototype chain by constructor name. */
function hasAncestorClassNamed(value: object, className: string): boolean {
for (let proto = Object.getPrototypeOf(value); proto; proto = Object.getPrototypeOf(proto)) {
if (proto.constructor?.name === className) return true;
}
return false;
}
function resolveInjectedStorageBackend(
storage: MastraCompositeStore,
configuredBackend?: 'libsql' | 'pg',
): 'libsql' | 'pg' {
if (configuredBackend) return configuredBackend;
if (storage instanceof LibSQLStore || hasAncestorClassNamed(storage, 'LibSQLStore')) return 'libsql';
if (storage instanceof PostgresStore || hasAncestorClassNamed(storage, 'PostgresStore')) return 'pg';
throw new Error('storageBackend is required when injecting a custom storage instance.');
}
export async function createMastraCodeAgentController(config?: MastraCodeConfig) {
const cwd = config?.cwd ?? process.cwd();
const homeDir = config?.homeDir ?? config?.initialState?.homeDir;
const configDir = config?.configDir ?? DEFAULT_CONFIG_DIR;
// The single session for this process, assigned once `createSession()` runs
// below. Config callbacks defined before then (e.g. notification stream
// options) read it lazily through this holder.
let activeSession: Session<MastraCodeState> | undefined;
// Same trick for the controller, which plugins reach through a lazy accessor.
// Plugins load well before the controller is constructed, and a closure over
// the `controller` binding itself would throw on early access rather than
// reporting "not ready yet", so the accessor reads this holder instead.
let pluginRuntimeController: AgentController<MastraCodeState> | undefined;
if (configDir !== DEFAULT_CONFIG_DIR) {
validateConfigDirName(configDir);
}View on GitHub (pinned to 75dd419e61)
Solutions
- Pass the backend explicitly: createMastraCodeAgentController({ storage: myStore, storageBackend: 'pg' }) (or 'libsql').
- Extend LibSQLStore or PostgresStore so the built-in detection recognizes your instance.
- If wrapping a store, keep the underlying store's class in the prototype chain so ancestor detection still matches.
Example fix
// before
createMastraCodeAgentController({ storage: new MyCustomStore() });
// after
createMastraCodeAgentController({ storage: new MyCustomStore(), storageBackend: 'pg' }); Defensive patterns
Strategy: validation
Validate before calling
const isLibsql = storage instanceof LibSQLStore;
const isPg = storage instanceof PostgresStore;
if (!isLibsql && !isPg && !storageBackend) {
throw new Error("storageBackend ('libsql' | 'pg') is required for custom storage instances");
} Type guard
function hasDeclaredBackend(
s: unknown,
): s is { storage: MastraCompositeStore; storageBackend: 'libsql' | 'pg' } {
const o = s as { storage?: unknown; storageBackend?: unknown };
return o.storage != null && (o.storageBackend === 'libsql' || o.storageBackend === 'pg');
} Try / catch
try {
controller = await createMastraCodeAgentController({ storage: myStore });
} catch (err) {
if (err instanceof Error && err.message.includes('storageBackend is required')) {
controller = await createMastraCodeAgentController({ storage: myStore, storageBackend: 'pg' });
} else { throw err; }
} Prevention
- Always pair a custom storage instance with an explicit storageBackend option.
- Prefer subclassing LibSQLStore/PostgresStore so built-in detection works.
- Avoid proxies/decorators that hide the store's prototype chain.
When it happens
Trigger: Passing a custom storage instance (not LibSQLStore/PostgresStore, and not a subclass of either) to createMastraCodeAgentController({ storage }) without also passing storageBackend: 'libsql' | 'pg' — and with no configuredBackend, detection falls through to the throw.
Common situations: Wrapping stores in a custom class or proxy (breaks instanceof and ancestor-name detection); using a community/third-party Mastra store; minification or renaming that defeats hasAncestorClassNamed.
Related errors
- Factory source control storage is unavailable
- AcpAgent does not support resuming suspended generate calls
- ACP prompt stopped before completing: ${response.stopReason}
- MastraAuthBetterAuth needs a database to build its own bette
- [Slack] Failed to resolve Mastra storage. Ensure your Mastra
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/8d344aff65d08bd1.
Report an issue: GitHub.