mastra-ai/mastra · error
Recording outputPath must be inside ${baseDir}
Error message
Recording outputPath must be inside ${baseDir} What it means
resolveOutputPath constrains the recording's output file to live inside the recordings base directory. It computes the relative path from baseDir and throws if the requested path is outside it, is the directory itself, or is a parent (path traversal guard).
Source
Thrown at packages/core/src/browser/recording/tools.ts:116
function generateRecordingId(): string {
return `rec_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
}
function recordingsDir(outputDir: string): string {
return resolve(outputDir);
}
function defaultOutputPath(id: string, outputDir: string): string {
return join(recordingsDir(outputDir), `${id}.avi`);
}
function resolveOutputPath(id: string, outputDir: string, requestedPath?: string): string {
const baseDir = recordingsDir(outputDir);
const outputPath = requestedPath ? resolve(requestedPath) : defaultOutputPath(id, outputDir);
const rel = relative(baseDir, outputPath);
if (rel === '' || isAbsolute(rel) || rel === '..' || rel.startsWith(`..${sep}`)) {
throw new Error(`Recording outputPath must be inside ${baseDir}`);
}
return outputPath;
}
function clearState(): void {
if (active?.autoStopTimer) {
clearTimeout(active.autoStopTimer);
}
if (active?.watchdogTimer) {
clearInterval(active.watchdogTimer);
}
active = null;
}
/** Internal: stop the screencast, ignoring errors. */
async function safeStop(stream: ScreencastStream): Promise<void> {
try {
await stream.stop();View on GitHub (pinned to 75dd419e61)
Solutions
- Pass an outputPath relative to the recordings directory (or omit it to use the default path)
- Use path.join(recordingsDir, 'my-video.avi') so the resolved path stays inside baseDir
- If you need output elsewhere, change outputDir when starting the recording rather than escaping via outputPath
Example fix
// before
browser_record({ action: 'start', outputPath: '/tmp/video.avi' });
// after
browser_record({ action: 'start', outputPath: 'session-42/video.avi' }); // resolved inside recordingsDir Defensive patterns
Strategy: validation
Validate before calling
import { resolve, relative, isAbsolute } from 'node:path';
const baseDir = recordingsDir(outputDir);
function isInsideBase(p) {
const rel = relative(baseDir, resolve(p));
return rel !== '' && !isAbsolute(rel) && rel !== '..' && !rel.startsWith('..' + require('node:path').sep);
}
if (!isInsideBase(requestedPath)) throw new Error('outputPath must stay inside the recordings directory'); Type guard
function isSafeOutputPath(p) {
const { resolve, relative, isAbsolute, sep } = require('node:path');
const rel = relative(baseDir, resolve(p));
return rel !== '' && !isAbsolute(rel) && rel !== '..' && !rel.startsWith(`..${sep}`);
} Try / catch
try {
await browser_record({ action: 'start', outputPath: userPath });
} catch (e) {
if (e.message.startsWith('Recording outputPath must be inside')) {
// fall back to the default output path
} else throw e;
} Prevention
- Pass relative paths inside the recordings directory
- Sanitize user-supplied paths before passing them to the tool
- Never build outputPath from untrusted input without confinement checks
When it happens
Trigger: Calling browser_record with an outputPath that is absolute elsewhere, points above the recordings dir via ../, or equals the base directory itself.
Common situations: Users passing /tmp/video.avi or C:\temp\out.avi while recordings are rooted elsewhere; '../escape.avi' style traversal (possibly from untrusted input); forgetting the tool sandboxes outputs by design.
Related errors
- Invalid route path: "${path}". Path cannot contain '..', '?'
- Worker ${label} must stay within the deployed artifact root.
- ${label} escapes workspace
- Path escapes workspace
- Invalid resourceId: ${resourceId}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/8f466c076c62af66.
Report an issue: GitHub.