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

  1. Pass the backend explicitly: createMastraCodeAgentController({ storage: myStore, storageBackend: 'pg' }) (or 'libsql').
  2. Extend LibSQLStore or PostgresStore so the built-in detection recognizes your instance.
  3. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/8d344aff65d08bd1. Report an issue: GitHub.