mastra-ai/mastra · error
MastraFactory: 'storage' is required. Pass a FactoryStorage
Error message
MastraFactory: 'storage' is required. Pass a FactoryStorage backend — e.g. new PgFactoryStorage({ connectionString }) from '@mastra/pg' for deployments, or new LibSQLFactoryStorage({ url }) from '@mastra/libsql' for local dev. What it means
The MastraFactory constructor requires a persistent FactoryStorage backend because the factory seeds a runtime registry and must persist state across processes/replicas. The constructor throws immediately when config.storage is missing, with guidance naming the two supported backends: PgFactoryStorage from '@mastra/pg' for deployments and LibSQLFactoryStorage from '@mastra/libsql' for local development. This is a fail-fast config validation so the app never boots half-configured.
Source
Thrown at mastracode/factory/src/factory.ts:292
for (const parent of KNOWN_PLATFORM_COOKIE_PARENTS) {
// Exact match → we're already on the parent, host-only is correct.
// Subdomain match → mint the parent-scoped cookie.
if (hostname === parent) return undefined;
if (hostname.endsWith(`.${parent}`)) return `.${parent}`;
}
return undefined;
}
export class MastraFactory {
readonly #config: MastraFactoryConfig;
#prepared: Awaited<ReturnType<typeof prepareAgentControllerMount>> | undefined;
#dispatcher: FactoryDecisionDispatcher | undefined;
#factoryProcessor: FactoryPhaseStateProcessor | undefined;
#preparing = false;
constructor(config: MastraFactoryConfig) {
if (!config?.storage) {
throw new Error(
"MastraFactory: 'storage' is required. Pass a FactoryStorage backend — e.g. " +
"new PgFactoryStorage({ connectionString }) from '@mastra/pg' for deployments, or " +
"new LibSQLFactoryStorage({ url }) from '@mastra/libsql' for local dev.",
);
}
this.#config = config;
}
/**
* Resolve feature readiness, wire every dependency explicitly, and assemble
* everything needed to construct the server-owned Mastra. Returns the args
* for the `new Mastra(...)` literal that must live in the entry file.
*/
async prepare(): Promise<MastraArgs> {
// Guard set synchronously (before the first await) so overlapping calls —
// not just strictly sequential ones — can't double-seed the runtime
// registry or double-run one-time adapter init.
if (this.#preparing) throw new Error('MastraFactory.prepare() called twice');View on GitHub (pinned to 75dd419e61)
Solutions
- Create a storage backend and pass it: storage: new LibSQLFactoryStorage({ url: 'file:./factory.db' }) for local dev, or storage: new PgFactoryStorage({ connectionString: process.env.DATABASE_URL }) for deployments.
- Install the matching package: pnpm add @mastra/libsql (local) or pnpm add @mastra/pg (deployed).
- Verify the config object passed to new MastraFactory() is not null/undefined and includes the storage key.
- For tests, use an in-memory/ephemeral file LibSQL url rather than omitting storage.
Example fix
// before
const factory = new MastraFactory({
publicUrl: 'https://factory.example.com',
});
// after
import { PgFactoryStorage } from '@mastra/pg';
const factory = new MastraFactory({
publicUrl: 'https://factory.example.com',
storage: new PgFactoryStorage({ connectionString: process.env.DATABASE_URL }),
}); Defensive patterns
Strategy: validation
Validate before calling
import { PgFactoryStorage } from '@mastra/pg';
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is required for factory storage');
const factory = new MastraFactory({
...config,
storage: new PgFactoryStorage({ connectionString: process.env.DATABASE_URL }),
});
if (!factory) throw new Error('MastraFactory construction failed'); Type guard
function hasStorage(config) {
return config != null && typeof config === 'object' && 'storage' in config && config.storage != null;
}
if (!hasStorage(factoryConfig)) throw new Error('MastraFactoryConfig.storage is required'); Try / catch
let factory;
try {
factory = new MastraFactory(config);
} catch (err) {
if (err.message.includes("'storage' is required")) {
throw new Error('Startup config error: supply a FactoryStorage backend (@mastra/pg or @mastra/libsql)', { cause: err });
}
throw err;
} Prevention
- Always include a storage backend in a shared buildFactoryConfig() helper so no call site can omit it
- Assert config completeness with a schema validator (zod) before constructing the factory
- Keep DATABASE_URL / LIBSQL_URL in env-checked startup code
- Add a boot-time unit test that constructs the factory with the real config builder
When it happens
Trigger: Calling new MastraFactory(config) where config is null/undefined or config.storage is undefined — e.g. building the config object conditionally and omitting storage, or passing only publicUrl/allowedOrigins/integrations.
Common situations: Following outdated docs or examples from before storage became mandatory; constructing the factory in a test harness with a minimal config; forgetting to instantiate a storage backend because the import from '@mastra/pg' or '@mastra/libsql' was removed during a refactor.
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
- MastraAuthBetterAuth needs a database to build its own bette
- Okta domain is required. Provide it in the options or set OK
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/60e68bee0cff99d8.
Report an issue: GitHub.