mastra-ai/mastra · error

MastraFactory: 'sandbox' must be a function constructing a M

Error message

MastraFactory: 'sandbox' must be a function constructing a MastraSandbox from a FactorySandboxContext.

What it means

After the sandbox fleet removal, 'sandbox' must be a function that takes a FactorySandboxContext and constructs a MastraSandbox. Any value that is defined but not a function (e.g. a string, number, boolean, or array) throws this generic type-validation error. Non-function, non-object values do not get the detailed migration message — that is reserved for legacy objects (error 313).

Source

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

    // The sandbox config is a bare callback constructing a session's sandbox
    // from intent. Shape-only validation: probing it with a synthetic ctx at
    // boot would construct against a fake session, so only the type is
    // checked.
    const sandboxConfig = this.#config.sandbox;
    if (sandboxConfig !== undefined && typeof sandboxConfig !== 'function') {
      // An object here is almost certainly the pre-callback config, which
      // described a fleet the factory managed itself. That fleet is gone:
      // sandboxes are per session and the host constructs them, so say what to
      // write instead rather than only naming the expected type.
      if (typeof sandboxConfig === 'object' && sandboxConfig !== null) {
        throw new Error(
          `MastraFactory: 'sandbox' is now a callback, not an options object. It receives a FactorySandboxContext and returns a MastraSandbox, so the host chooses the provider per session:\n` +
            `  sandbox: ctx => new E2BSandbox({ id: ctx.sessionId })\n` +
            `The old options map three ways: 'machine' becomes the provider instance you construct inside the callback (one per session instead of one cloned template); 'workdir' is gone — remote providers clone into the VM's home directory and local providers check out under their own workingDirectory; 'maxSandboxes' is gone with the sandbox fleet — there is one sandbox per session and no pool to cap. Omit 'sandbox' entirely to disable sandboxes.`,
        );
      }
      throw new Error(
        `MastraFactory: 'sandbox' must be a function constructing a MastraSandbox from a FactorySandboxContext.`,
      );
    }

    const workspaceRegistry = new FactoryWorkspaceRegistry();

    // One shared OAuth state signer per boot. The deploy entry supplies a
    // replica-stable secret when needed; otherwise local development gets a
    // per-process random signer (`stable: false`).
    const stateSigner = createStateSigner(this.#config.stateSecret);

    // One-time provider initialization with factory-level context (e.g.
    // better-auth builds its default instance on the backend's auth
    // database, WorkOS derives its redirect URI from the public URL).
    // Failures surface here, at prepare() — a misconfigured provider must
    // not boot.
    if (auth && hasAuthInit(auth)) {
      await timedPhase('prepare.auth.init', () =>

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Wrap the construction in a function: sandbox: ctx => new E2BSandbox({ id: ctx.sessionId }).
  2. If you intended to disable sandboxes, delete the sandbox key instead of passing a falsy placeholder.
  3. Remove any 'as any' casts so TypeScript flags the non-function type at compile time.
  4. Ensure the callback returns a MastraSandbox instance, not a Promise or class reference.

Example fix

// before
sandbox: E2BSandbox

// after
sandbox: ctx => new E2BSandbox({ id: ctx.sessionId })
Defensive patterns

Strategy: type-guard

Validate before calling

if (config.sandbox !== undefined && typeof config.sandbox !== 'function') {
  throw new TypeError('sandbox must be a function: ctx => new E2BSandbox({ id: ctx.sessionId })');
}

Type guard

function isSandboxFactory(v) {
  return typeof v === 'function' && v.prototype?.execute !== undefined || typeof v === 'function';
}
// stricter: require it to accept a context and be a declared arrow/constructor
if (config.sandbox !== undefined && typeof config.sandbox !== 'function') throw new TypeError('sandbox must be a callback');

Try / catch

try {
  await factory.prepare();
} catch (err) {
  if (err.message.includes("'sandbox' must be a function")) {
    throw new Error('Config error: sandbox must be a callback returning a MastraSandbox', { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Passing sandbox: true, a provider class without new (sandbox: E2BSandbox), a string, or any other non-function value in the factory config; also passing a Promise or a constructed-but-non-MastraSandbox value that is not a function.

Common situations: Forgetting the arrow wrapper and assigning the constructor/class itself; typo'd config where sandbox gets a boolean flag; JSON-loaded config where functions cannot be expressed.

Related errors


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