mastra-ai/mastra · error
@mastra/deployer-sandbox/client is server-only: resolving a
Error message
@mastra/deployer-sandbox/client is server-only: resolving a sandbox requires provider credentials that must never reach the browser. Proxy requests through your own backend instead (see createSandboxHandler / createSandboxProxy).
What it means
The @mastra/deployer-sandbox/client module must run only on the server: resolving a sandbox requires provider credentials that would leak if executed in the browser. assertServerOnly() checks for the presence of globalThis.window and throws if the module is imported/executed in a browser environment. The library directs you to proxy through your own backend via createSandboxHandler/createSandboxProxy.
Source
Thrown at deployers/sandbox/src/client/index.ts:23
* (e.g. VERCEL_TOKEN) — importing it in the browser would ship those
* credentials to the client. Use the proxy/handler patterns instead so the
* browser only ever talks to your own domain.
*/
import { supportsNetworking } from '@mastra/core/workspace';
import type { WorkspaceSandbox } from '@mastra/core/workspace';
import {
DEFAULT_PORT,
getInfoSafe,
killPreviousServer,
launchServer,
resolveRemoteDir,
tailServerLog,
waitForHealthy,
} from '../shared';
function assertServerOnly(): void {
if (typeof (globalThis as { window?: unknown }).window !== 'undefined') {
throw new Error(
'@mastra/deployer-sandbox/client is server-only: resolving a sandbox requires provider credentials ' +
'that must never reach the browser. Proxy requests through your own backend instead ' +
'(see createSandboxHandler / createSandboxProxy).',
);
}
}
export type DeploymentStatus = 'running' | 'stopped' | 'unknown';
export interface GetDeploymentOptions {
/**
* The sandbox to resolve. Provider construction is identity — e.g.
* `new VercelSandbox({ sandboxName: 'my-preview', ports: [4111] })` resolves
* the same sandbox from any process.
*/
sandbox: WorkspaceSandbox;
/** Port the Mastra server listens on. Defaults to 4111. */
port?: number;View on GitHub (pinned to 75dd419e61)
Solutions
- Move the getDeployment() call to server code: a route handler, server action, or API route.
- Expose it to the browser via createSandboxHandler mounted on your backend, and call that endpoint from the client.
- Or use createSandboxProxy to proxy sandbox requests through your server instead of importing the client module in the browser.
- Verify with 'server-only' import conventions / build-time checks that the module is absent from client bundles.
Example fix
// before ('use client' component)
'use client';
import { getDeployment } from '@mastra/deployer-sandbox/client';
const dep = await getDeployment({ sandboxId: 'sbx_1' });
// after (server route + fetch)
// app/api/sandbox/route.ts: export { POST } from createSandboxHandler(...)
const res = await fetch('/api/sandbox', { method: 'POST', body: JSON.stringify({ sandboxId: 'sbx_1' }) }); Defensive patterns
Strategy: validation
Validate before calling
if (typeof window !== 'undefined') {
throw new Error('Sandbox resolution must run server-side; call /api/sandbox instead');
} Type guard
function isServerRuntime(): boolean {
return typeof (globalThis as { window?: unknown }).window === 'undefined';
} Try / catch
try {
const dep = await getDeployment({ sandboxId });
} catch (err) {
if ((err as Error).message.includes('server-only')) {
// redirect to server proxy endpoint
const res = await fetch('/api/sandbox', { method: 'POST', body: JSON.stringify({ sandboxId }) });
} else throw err;
} Prevention
- Keep all @mastra/deployer-sandbox/client imports in server-only files (route handlers, server actions).
- Add import 'server-only' at the top of server modules so bundlers error on client inclusion.
- Audit client bundles for sandbox provider credentials (grep the build output for provider/token keys).
- Use createSandboxHandler/createSandboxProxy as the boundary between browser and sandbox resolution.
When it happens
Trigger: Importing @mastra/deployer-sandbox/client (e.g. calling getDeployment) from client-side React/Next.js code, or bundling it into a browser bundle where window is defined at execution time.
Common situations: Calling getDeployment() inside a 'use client' component or useEffect in a Next.js app, accidentally tree-shaking the server-only marker out so the import isn't blocked at build time, or sharing one module between client and server routes.
Related errors
- ${errorData.message || 'Invalid email or password'}
- Cookie password must be at least 32 characters for SSO. Set
- [MastraAuthGoogle] GOOGLE_COOKIE_PASSWORD is required for Go
- Invalid Google ID token nonce
- Invalid email or password
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/efd9e74cadbeea27.
Report an issue: GitHub.