mastra-ai/mastra · error

[FilesystemStorage] path must be a non-empty relative path.

Error message

[FilesystemStorage] path must be a non-empty relative path.

What it means

assertRelativePath enforces that file paths stored in FilesystemStorage are non-empty, relative, and composed only of safe segments — no leading '/', no empty, '.', or '..' segments. This prevents path traversal and absolute-path writes. Violating any of these throws this error via validateFiles.

Source

Thrown at mastracode/factory/src/storage/domains/filesystem/base.ts:53

interface FilesystemSnapshotDbRow extends Record<string, unknown> {
  id: string;
  resource_id: string;
  thread_id: string;
  files: FilesystemFile[];
  captured_at: Date;
}

function assertIdentifier(value: string, label: string): void {
  if (!value.trim()) throw new Error(`[FilesystemStorage] ${label} must not be empty.`);
}

function assertRelativePath(value: string): void {
  if (
    !value ||
    value.startsWith('/') ||
    value.split('/').some(segment => !segment || segment === '.' || segment === '..')
  ) {
    throw new Error('[FilesystemStorage] path must be a non-empty relative path.');
  }
}

function assertScope(args: { resourceId: string; threadId: string }): void {
  assertIdentifier(args.resourceId, 'resourceId');
  assertIdentifier(args.threadId, 'threadId');
}

function validateFiles(files: FilesystemFile[]): void {
  const paths = new Set<string>();

  for (const file of files) {
    assertRelativePath(file.path);
    if (paths.has(file.path)) throw new Error(`[FilesystemStorage] duplicate file path: ${file.path}`);
    paths.add(file.path);
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Strip the storage root prefix so paths are relative
  2. Sanitize/reject segments equal to '.', '..' or empty before calling the API
  3. Normalize user-provided filenames (remove traversal, collapse slashes)
  4. Use a path-join helper that always yields relative, clean segments

Example fix

// before
await storage.replaceFiles({ resourceId, threadId, files: [{ path: `/uploads/${userFile}`, ... }] });
// after
const safe = userFile.replaceAll('..', '').replace(/^\/+/, '');
await storage.replaceFiles({ resourceId, threadId, files: [{ path: `uploads/${safe}`, ... }] });
Defensive patterns

Strategy: validation

Validate before calling

function isSafeRelativePath(p: string): boolean {
  return !!p && !p.startsWith('/') && p.split('/').every(s => !!s && s !== '.' && s !== '..');
}
if (!isSafeRelativePath(file.path)) throw new Error(`Unsafe path: ${file.path}`);

Type guard

function isFilesystemFile(f: unknown): f is FilesystemFile {
  return typeof f === 'object' && f !== null &&
    typeof (f as FilesystemFile).path === 'string' && isSafeRelativePath((f as FilesystemFile).path);
}

Try / catch

try {
  await storage.replaceFiles({ resourceId, threadId, files });
} catch (e) {
  if (e instanceof Error && e.message.includes('path must be a non-empty relative path')) {
    throw new BadRequest(`Invalid file path in batch: ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling a file-writing method (e.g. via replaceFiles → validateFiles) with a FilesystemFile whose path is '', starts with '/', or contains segments like '..', './', or double slashes.

Common situations: Joining an absolute filesystem path directly instead of a storage-relative path; user-supplied filenames containing '..'; path.normalize output retaining a leading slash; string concatenation producing '//'.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/4fb2d907b78b6874. Report an issue: GitHub.