mastra-ai/mastra · error
Worker ${label} must stay within the deployed artifact root.
Error message
Worker ${label} must stay within the deployed artifact root. What it means
validateRelativePath requires a path used inside the deployed worker artifact to be non-empty, relative, and not escape the artifact root after normalization. It guards workingDirectory and file-type worker input paths so uploads and cd targets can never resolve outside the deployed directory (path traversal defense).
Source
Thrown at deployers/sandbox/src/worker.ts:206
for (const name of Object.keys(resourceLimits)) {
if (!knownLimits.has(name)) throw new Error(`Unknown worker resource limit: ${name}.`);
}
for (const [name, value] of [
['cpuTimeSeconds', resourceLimits.cpuTimeSeconds],
['addressSpaceBytes', resourceLimits.addressSpaceBytes],
['fileSizeBytes', resourceLimits.fileSizeBytes],
['openFiles', resourceLimits.openFiles],
] as const) {
if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
throw new Error(`Worker resourceLimits.${name} must be a positive safe integer.`);
}
}
}
}
function validateRelativePath(value: string, label: string): void {
if (!value || posix.isAbsolute(value) || posix.normalize(value).startsWith('..')) {
throw new Error(`Worker ${label} must stay within the deployed artifact root.`);
}
}
function validateInput(input: SandboxWorkerInput | undefined): void {
if (input?.type === 'file') validateRelativePath(input.path, 'input file path');
}
function normalizeResourceLimits(
limits: SandboxWorkerResourceLimits | undefined,
): NormalizedResourceLimits | undefined {
if (!limits || Object.values(limits).every(value => value === undefined)) return undefined;
return {
cpuTimeSeconds: limits.cpuTimeSeconds,
addressSpaceBytes: limits.addressSpaceBytes,
addressSpaceKilobytes:
limits.addressSpaceBytes === undefined ? undefined : Math.floor(limits.addressSpaceBytes / 1024),
fileSizeBytes: limits.fileSizeBytes,
fileSizeBlocks: limits.fileSizeBytes === undefined ? undefined : Math.floor(limits.fileSizeBytes / 512),View on GitHub (pinned to 75dd419e61)
Solutions
- Use a relative path such as '.', 'dist', or 'bin/server.js' that resolves inside the artifact you deploy in dir.
- Strip leading slashes and resolve '..' segments against your intended base before calling, or use posix.resolve(base, p) and verify the result starts with the base.
- For data living outside the artifact, stage it as stdin input (input: { type: 'stdin', data }) instead of a file path.
- For relaunch input, ensure the file path is relative to the original remoteDir, not the host filesystem.
Example fix
// before
await deployWorkerToSandbox({ sandbox, dir: './worker', command: 'node', workingDirectory: '/opt/worker/dist' });
await deployWorkerToSandbox({ sandbox, dir: './worker', command: 'node', input: { type: 'file', path: '../payload.json' } });
// after
await deployWorkerToSandbox({ sandbox, dir: './worker', command: 'node', workingDirectory: 'dist' });
await deployWorkerToSandbox({ sandbox, dir: './worker', command: 'node', input: { type: 'file', path: 'payload.json' } }); Defensive patterns
Strategy: validation
Validate before calling
import { posix } from 'node:path';
function assertSafeRelativePath(value, label) {
if (!value || posix.isAbsolute(value) || posix.normalize(value).startsWith('..')) {
throw new Error(`${label} must be a relative path inside the artifact root, got: ${value}`);
}
}
assertSafeRelativePath(options.workingDirectory ?? '.', 'workingDirectory');
if (options.input?.type === 'file') assertSafeRelativePath(options.input.path, 'input path'); Type guard
function isSafeRelativePath(value) {
return typeof value === 'string' && value.length > 0 &&
!posix.isAbsolute(value) && !posix.normalize(value).startsWith('..');
} Try / catch
try {
await deployWorkerToSandbox(options);
} catch (error) {
if (error instanceof Error && error.message.includes('must stay within the deployed artifact root')) {
console.error(`Make ${error.message.match(/Worker (.+) must/)?.[1]} a relative path like 'dist' or 'data/input.json'`);
} else throw error;
} Prevention
- Always join user-supplied segments with posix.resolve(base, p) and assert the result starts with base before passing.
- Never copy absolute host paths into workingDirectory or input file paths.
- Sanitize config-sourced paths by stripping leading '/' and rejecting '..' segments early in your config loader.
- Use stdin input instead of file paths for data generated outside the deployed artifact.
When it happens
Trigger: Passing deployWorkerToSandbox options.workingDirectory = '/app' (absolute), '' (empty), or '../shared'; or passing input: { type: 'file', path: '../../etc/passwd' } (also via deployment.relaunch({ input }) which re-validates). Any path whose posix.normalize starts with '..' triggers it.
Common situations: Using an absolute host path copied from local machine config; building paths with string concatenation where a parent dir slips in ('..' segments); defaulting to '' when a config key is missing; trying to mount or read shared files outside the artifact root; Windows-style absolute paths like 'C:\data' after normalization still count as relative but leading-slash POSIX paths do not.
Related errors
- Invalid route path: "${path}". Path cannot contain '..', '?'
- ${label} escapes workspace
- Path escapes workspace
- Invalid resourceId: ${resourceId}
- Recording outputPath must be inside ${baseDir}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/e3d60d0b78db4333.
Report an issue: GitHub.