{"record":{"id":"efd9e74cadbeea27","repo":"mastra-ai/mastra","slug":"mastra-deployer-sandbox-client-is-server-only-re","errorCode":null,"errorMessage":"@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).","messagePattern":"@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\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"deployers/sandbox/src/client/index.ts","lineNumber":23,"sourceCode":" * (e.g. VERCEL_TOKEN) — importing it in the browser would ship those\n * credentials to the client. Use the proxy/handler patterns instead so the\n * browser only ever talks to your own domain.\n */\nimport { supportsNetworking } from '@mastra/core/workspace';\nimport type { WorkspaceSandbox } from '@mastra/core/workspace';\nimport {\n  DEFAULT_PORT,\n  getInfoSafe,\n  killPreviousServer,\n  launchServer,\n  resolveRemoteDir,\n  tailServerLog,\n  waitForHealthy,\n} from '../shared';\n\nfunction assertServerOnly(): void {\n  if (typeof (globalThis as { window?: unknown }).window !== 'undefined') {\n    throw new Error(\n      '@mastra/deployer-sandbox/client is server-only: resolving a sandbox requires provider credentials ' +\n        'that must never reach the browser. Proxy requests through your own backend instead ' +\n        '(see createSandboxHandler / createSandboxProxy).',\n    );\n  }\n}\n\nexport type DeploymentStatus = 'running' | 'stopped' | 'unknown';\n\nexport interface GetDeploymentOptions {\n  /**\n   * The sandbox to resolve. Provider construction is identity — e.g.\n   * `new VercelSandbox({ sandboxName: 'my-preview', ports: [4111] })` resolves\n   * the same sandbox from any process.\n   */\n  sandbox: WorkspaceSandbox;\n  /** Port the Mastra server listens on. Defaults to 4111. */\n  port?: number;","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/deployers/sandbox/src/client/index.ts#L5-L41","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before ('use client' component)\n'use client';\nimport { getDeployment } from '@mastra/deployer-sandbox/client';\nconst dep = await getDeployment({ sandboxId: 'sbx_1' });\n// after (server route + fetch)\n// app/api/sandbox/route.ts: export { POST } from createSandboxHandler(...)\nconst res = await fetch('/api/sandbox', { method: 'POST', body: JSON.stringify({ sandboxId: 'sbx_1' }) });","handlingStrategy":"validation","validationCode":"if (typeof window !== 'undefined') {\n  throw new Error('Sandbox resolution must run server-side; call /api/sandbox instead');\n}","typeGuard":"function isServerRuntime(): boolean {\n  return typeof (globalThis as { window?: unknown }).window === 'undefined';\n}","tryCatchPattern":"try {\n  const dep = await getDeployment({ sandboxId });\n} catch (err) {\n  if ((err as Error).message.includes('server-only')) {\n    // redirect to server proxy endpoint\n    const res = await fetch('/api/sandbox', { method: 'POST', body: JSON.stringify({ sandboxId }) });\n  } else throw err;\n}","preventionTips":["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."],"tags":["browser","server-only","security","credentials"],"backgroundTag":"server-only-module-in-browser","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}