mastra-ai/mastra · error

Root is not approved for rendered workspace access

Error message

Root is not approved for rendered workspace access

What it means

assertApprovedRenderedRoot validates that a requested workspace root (after relative-path sanitization) is one of the pre-approved rendered roots in APPROVED_RENDERED_ROOTS. The factory throws "Root is not approved for rendered workspace access" when the caller asks the fs routes to serve a root that the deployment has not explicitly allow-listed, preventing arbitrary directories from being exposed for rendered output browsing.

Source

Thrown at mastracode/factory/src/routes/fs.ts:227

  } catch {
    return null;
  }
}

function assertRelativePath(path: string, label: string): string {
  const trimmed = path.trim();
  if (!trimmed) throw new Error(`Missing required query param: ${label}`);
  if (isAbsolute(trimmed)) throw new Error(`${label} must be relative`);
  if (trimmed.split(/[\\/]+/).includes('..')) throw new Error(`${label} escapes workspace`);
  const normalized = resolve('/', trimmed).slice(1);
  if (!normalized || normalized === '..' || normalized.startsWith(`..${sep}`))
    throw new Error(`${label} escapes workspace`);
  return normalized;
}

function assertApprovedRenderedRoot(renderedRoot: string): string {
  const safeRoot = assertRelativePath(renderedRoot, 'root');
  if (!APPROVED_RENDERED_ROOTS.has(safeRoot)) throw new Error('Root is not approved for rendered workspace access');
  return safeRoot;
}

async function confinedWorkspacePath(
  root: string,
  workspacePath: string,
): Promise<{ resolvedRoot: string; workspace: string }> {
  const resolvedRoot = await realOrResolved(resolveFsRoot(root));
  const candidate = isAbsolute(workspacePath) ? resolve(workspacePath) : resolve(resolvedRoot, workspacePath);
  const workspace = await realPathWithinRoot(candidate, resolvedRoot);
  if (!workspace) throw new Error('Path is outside the browsable root');
  return { resolvedRoot, workspace };
}

async function confinedWorkspaceRelativePath(
  root: string,
  workspacePath: string,
  relativePath: string,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Use one of the approved root values exactly as listed in APPROVED_RENDERED_ROOTS in mastracode/factory/src/routes/fs.ts
  2. If a new root is legitimately needed, add its sanitized relative form to APPROVED_RENDERED_ROOTS and redeploy
  3. Log/inspect the sanitized root (assertRelativePath output) to spot mismatched case, slashes, or nesting
  4. Verify the route is the right one: rendered-workspace routes only accept rendered roots; generic browsing routes take the configured workspace root instead

Example fix

// before
const res = await fetch('/api/fs/rendered?root=sessions/tmp-123/output');
// after (add to APPROVED_RENDERED_ROOTS first, or use an approved root)
const APPROVED = ['rendered', 'sessions/rendered'];
const root = APPROVED.includes('rendered') ? 'rendered' : APPROVED[0];
const res = await fetch(`/api/fs/rendered?root=${root}`);
Defensive patterns

Strategy: validation

Validate before calling

const APPROVED_RENDERED_ROOTS = new Set(['rendered']); // mirror of the server-side set
function useApprovedRoot(root: string): string {
  const safe = root.trim().replace(/^\/+/, '');
  if (!APPROVED_RENDERED_ROOTS.has(safe)) throw new Error(`root not approved: ${safe}`);
  return safe;
}

Type guard

function isApprovedRoot(root: unknown): root is string {
  return typeof root === 'string' && APPROVED_RENDERED_ROOTS.has(root.trim().replace(/^\/+/, ''));
}

Try / catch

try {
  return await renderedRoute({ root });
} catch (err) {
  if (err instanceof Error && err.message.includes('not approved for rendered workspace')) {
    root = 'rendered'; // fall back to the default approved root
    return await renderedRoute({ root });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling routes that use safeRoot, readWorkspaceFile, or readSessionWorkspaceFile with ?root=<something> whose sanitized value is not present in APPROVED_RENDERED_ROOTS — e.g. a typo'd root name, a session-specific directory not on the list, or a root added to the filesystem but not to the allow-list constant.

Common situations: New workspace/session directories created by the pipeline but APPROVED_RENDERED_ROOTS not updated; case or trailing-slash mismatches after sanitization; deploying code with a hardcoded root name that differs per environment; tests referencing fixture roots not in the allow-list.

Related errors


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