{"record":{"id":"60e68bee0cff99d8","repo":"mastra-ai/mastra","slug":"mastrafactory-storage-is-required-pass-a-facto","errorCode":null,"errorMessage":"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.","messagePattern":"MastraFactory: 'storage' is required\\. Pass a FactoryStorage backend — e\\.g\\. new PgFactoryStorage\\((.+?)\\) from '@mastra/pg' for deployments, or new LibSQLFactoryStorage\\((.+?)\\) from '@mastra/libsql' for local dev\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/factory.ts","lineNumber":292,"sourceCode":"  for (const parent of KNOWN_PLATFORM_COOKIE_PARENTS) {\n    // Exact match → we're already on the parent, host-only is correct.\n    // Subdomain match → mint the parent-scoped cookie.\n    if (hostname === parent) return undefined;\n    if (hostname.endsWith(`.${parent}`)) return `.${parent}`;\n  }\n  return undefined;\n}\n\nexport class MastraFactory {\n  readonly #config: MastraFactoryConfig;\n  #prepared: Awaited<ReturnType<typeof prepareAgentControllerMount>> | undefined;\n  #dispatcher: FactoryDecisionDispatcher | undefined;\n  #factoryProcessor: FactoryPhaseStateProcessor | undefined;\n  #preparing = false;\n\n  constructor(config: MastraFactoryConfig) {\n    if (!config?.storage) {\n      throw new Error(\n        \"MastraFactory: 'storage' is required. Pass a FactoryStorage backend — e.g. \" +\n          \"new PgFactoryStorage({ connectionString }) from '@mastra/pg' for deployments, or \" +\n          \"new LibSQLFactoryStorage({ url }) from '@mastra/libsql' for local dev.\",\n      );\n    }\n    this.#config = config;\n  }\n\n  /**\n   * Resolve feature readiness, wire every dependency explicitly, and assemble\n   * everything needed to construct the server-owned Mastra. Returns the args\n   * for the `new Mastra(...)` literal that must live in the entry file.\n   */\n  async prepare(): Promise<MastraArgs> {\n    // Guard set synchronously (before the first await) so overlapping calls —\n    // not just strictly sequential ones — can't double-seed the runtime\n    // registry or double-run one-time adapter init.\n    if (this.#preparing) throw new Error('MastraFactory.prepare() called twice');","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/factory.ts#L274-L310","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst factory = new MastraFactory({\n  publicUrl: 'https://factory.example.com',\n});\n\n// after\nimport { PgFactoryStorage } from '@mastra/pg';\nconst factory = new MastraFactory({\n  publicUrl: 'https://factory.example.com',\n  storage: new PgFactoryStorage({ connectionString: process.env.DATABASE_URL }),\n});","handlingStrategy":"validation","validationCode":"import { PgFactoryStorage } from '@mastra/pg';\nif (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is required for factory storage');\nconst factory = new MastraFactory({\n  ...config,\n  storage: new PgFactoryStorage({ connectionString: process.env.DATABASE_URL }),\n});\nif (!factory) throw new Error('MastraFactory construction failed');","typeGuard":"function hasStorage(config) {\n  return config != null && typeof config === 'object' && 'storage' in config && config.storage != null;\n}\nif (!hasStorage(factoryConfig)) throw new Error('MastraFactoryConfig.storage is required');","tryCatchPattern":"let factory;\ntry {\n  factory = new MastraFactory(config);\n} catch (err) {\n  if (err.message.includes(\"'storage' is required\")) {\n    throw new Error('Startup config error: supply a FactoryStorage backend (@mastra/pg or @mastra/libsql)', { cause: err });\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["configuration","constructor","storage","missing-required-argument"],"backgroundTag":"missing-required-config-option","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}