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
- Wrap the construction in a function: sandbox: ctx => new E2BSandbox({ id: ctx.sessionId }).
- If you intended to disable sandboxes, delete the sandbox key instead of passing a falsy placeholder.
- Remove any 'as any' casts so TypeScript flags the non-function type at compile time.
- 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
- Never assign a class/constructor directly; wrap with ctx => new Provider({...})
- Type the config field as (ctx: FactorySandboxContext) => MastraSandbox so non-functions fail compile
- Don't load function-valued config from JSON; keep it in code
- Remove the sandbox key entirely when sandboxes are disabled
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
- Could not resolve the sandbox home directory. Pass `remoteDi
- Unknown worker resource limit: ${name}.
- MastraFactory: 'sandbox' is now a callback, not an options o
- commit-failed
- Sandbox provider '${sandbox.provider}' does not support exec
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/7847e67507c5574e.
Report an issue: GitHub.