mastra-ai/mastra · error
MastraFactory: 'sandbox' is now a callback, not an options o
Error message
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:
sandbox: ctx => new E2BSandbox({ id: ctx.sessionId })
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. What it means
The factory's 'sandbox' option was redesigned: it is now a callback receiving a FactorySandboxContext and returning a MastraSandbox, letting the host choose the provider per session. Passing a plain object (the legacy fleet-style options with 'machine', 'workdir', 'maxSandboxes') throws this migration-targeted message explaining how each old option maps to the new model. Omitting 'sandbox' entirely disables sandboxes.
Source
Thrown at mastracode/factory/src/factory.ts:428
comments: workItemCommentsStorage,
workItems: workItemsStorage,
projects: factoryProjectsStorage,
channelIdentity: channelIdentityStorage,
audit: auditDomain,
});
// 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.View on GitHub (pinned to 75dd419e61)
Solutions
- Replace the options object with a callback that constructs a provider per session: sandbox: ctx => new E2BSandbox({ id: ctx.sessionId }).
- If you used 'machine', construct the provider instance yourself inside the callback (one per session, no cloned template).
- Drop 'workdir' — remote providers clone into the VM home dir; local providers manage their own workingDirectory.
- Drop 'maxSandboxes' — there is one sandbox per session and no pool to cap.
- If sandboxes are not needed, remove the sandbox key entirely to disable them.
Example fix
// before
sandbox: { machine: 'base', workdir: '/tmp/sb', maxSandboxes: 10 }
// after
import { E2BSandbox } from '@mastra/e2b';
sandbox: ctx => new E2BSandbox({ id: ctx.sessionId }) Defensive patterns
Strategy: type-guard
Validate before calling
import { z } from 'zod';
const sandboxSchema = z.function();
if (config.sandbox !== undefined && !sandboxSchema.safeParse(config.sandbox).success) {
throw new Error("config.sandbox must be a callback: ctx => new E2BSandbox({ id: ctx.sessionId })");
} Type guard
function isSandboxCallback(v) {
return v === undefined || (typeof v === 'function' && v.length <= 1);
}
if (!isSandboxCallback(config.sandbox)) throw new Error('sandbox must be a FactorySandboxContext callback'); Try / catch
try {
const factory = new MastraFactory(config);
await factory.prepare();
} catch (err) {
if (err.message.includes("'sandbox' is now a callback")) {
throw new Error('Migration needed: sandbox config uses the removed fleet options object', { cause: err });
}
throw err;
} Prevention
- Update config types to the callback signature so legacy objects fail typecheck
- Search the codebase for 'maxSandboxes'/'workdir' to find leftover legacy config
- Copy the callback pattern from current docs: ctx => new E2BSandbox({ id: ctx.sessionId })
- Add a startup test that runs prepare() with the real config
When it happens
Trigger: Setting sandbox in the factory config to a non-null object instead of a function, e.g. sandbox: { machine: {...}, workdir: '/tmp/sb', maxSandboxes: 10 } — the old fleet configuration shape from before the callback API.
Common situations: Upgrading from a previous factory version that accepted a fleet options object; copying config from old docs or an example repo; type assertions (as any) hiding the type error that would otherwise catch the object at compile time.
Related errors
- toAISdkFormat() has been deprecated. Please use toAISdkStrea
- Could not resolve the sandbox home directory. Pass `remoteDi
- Unknown worker resource limit: ${name}.
- MastraFactory: 'sandbox' must be a function constructing a M
- commit-failed
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/3a805f1bd96820d9.
Report an issue: GitHub.