mastra-ai/mastra · error

MastraFactory.finalize() called before prepare()

Error message

MastraFactory.finalize() called before prepare()

What it means

finalize() is the post-construct boot step: it initializes the factory controller (which inherits the constructed Mastra's storage) and starts its workers, and must run after the entry file has executed new Mastra(prepare()'s args). It checks the internal #prepared flag and throws if prepare() has not successfully completed, since finalize would otherwise boot a controller against uninitialized state.

Source

Thrown at mastracode/factory/src/factory.ts:999

    return {
      ...prepared.mastraArgs,
      // Same provider on `studio.auth` as on `server.auth` (buildServerConfig):
      // deployed factories must authenticate BOTH plain API callers and Studio
      // requests (`x-mastra-client-type: studio` routes to `studio.auth`).
      ...(auth ? { studio: { auth } } : {}),
      ...(integrationWorkers.length > 0 ? { workers: integrationWorkers } : {}),
    };
  }

  /**
   * Post-construct boot: initialize the controller (which inherits the
   * constructed Mastra's storage) and start its workers. Call AFTER the entry
   * has run `new Mastra(prepare()'s args)`.
   */
  async finalize(): Promise<void> {
    if (!this.#prepared) {
      throw new Error('MastraFactory.finalize() called before prepare()');
    }
    await timedPhase('finalize.controller', () => this.#prepared!.finalize());
    await timedPhase(
      'finalize.reconcileBoundThreads',
      () => this.#factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),
    );
    this.#dispatcher?.start();
  }

  /** Stop Factory-owned background dispatch before the host process shuts down. */
  async shutdown(): Promise<void> {
    await this.#dispatcher?.stop();
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Call prepare() first, construct the Mastra instance with its returned args, then call finalize(): const args = await factory.prepare(); const mastra = new Mastra(args); await factory.finalize();
  2. Ensure the ordering in your entry file: prepare → new Mastra(args) → finalize.
  3. If this is a short-lived script, decide whether you need finalize at all — it boots controller workers meant for a running server.
  4. Check that hot-reload or lazy-init logic isn't invoking finalize before the prepare promise resolves.

Example fix

// before
await factory.finalize(); // throws

// after
const args = await factory.prepare();
export const mastra = new Mastra(args);
await factory.finalize();
Defensive patterns

Strategy: try-catch

Validate before calling

// enforce the startup sequence in a helper
export async function bootFactory(config) {
  const factory = new MastraFactory(config);
  const args = await factory.prepare();
  const mastra = new Mastra(args);
  await factory.finalize();
  return { factory, mastra };
}

Type guard

// only finalize after prepare has resolved and Mastra was constructed
let prepared = false;
async function safeFinalize(factory) {
  if (!prepared) throw new Error('finalize() requires prepare() + new Mastra(args) first');
  return factory.finalize();
}

Try / catch

try {
  await factory.finalize();
} catch (err) {
  if (err.message.includes('called before prepare()')) {
    throw new Error('Boot order bug: run prepare() and new Mastra(args) before finalize()', { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling await factory.finalize() before (or instead of) calling await factory.prepare() and constructing new Mastra(args) — e.g. skipping prepare in a script, or ordering finalize before prepare in the entry file.

Common situations: Writing a standalone migration/CLI script that calls finalize directly; reordering statements during a refactor so finalize runs first; copying the finalize call into a new entry file without the prepare + new Mastra sequence.

Related errors


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