mastra-ai/mastra · error · HTTPException

Workspace is read-only

Error message

Workspace is read-only

What it means

HTTPException 403 thrown when the target workspace's filesystem is marked readOnly. Installing skill files requires write access, so the handler rejects the request even though the workspace and its filesystem exist.

Source

Thrown at packages/server/src/server/handlers/workspace.ts:1434

  responseSchema: skillsShInstallResponseSchema,
  summary: 'Install skill from Skills API',
  description: 'Installs a skill by fetching files from the Skills API and writing to workspace filesystem.',
  tags: ['Workspace', 'Skills'],
  handler: async ({ mastra, workspaceId, owner, repo, skillName, mount }) => {
    try {
      requireWorkspaceV1Support();

      const workspace = await getWorkspaceById(mastra, workspaceId);
      if (!workspace) {
        throw new HTTPException(404, { message: 'Workspace not found' });
      }

      if (!workspace.filesystem) {
        throw new HTTPException(400, { message: 'Workspace filesystem not available' });
      }

      if (workspace.filesystem.readOnly) {
        throw new HTTPException(403, { message: 'Workspace is read-only' });
      }

      // Fetch skill files from the Skills API
      const result = await fetchSkillFiles(owner, repo, skillName);
      if (!result || result.files.length === 0) {
        throw new HTTPException(404, {
          message: `Could not find skill "${skillName}" in ${owner}/${repo}.`,
        });
      }

      // Validate skill name to prevent path traversal
      const safeSkillId = assertSafeSkillName(result.skillId);
      const installPath = buildSkillInstallPath(workspace.filesystem, safeSkillId, mount);

      // Ensure the skills directory exists
      try {
        await workspace.filesystem.mkdir(installPath, { recursive: true });
      } catch {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Use a writable workspace for skill installation.
  2. Change the workspace's readOnly flag / mount it read-write if policy allows, then retry.
  3. Write the skill files through an admin or privileged flow that has write access to the underlying storage.

Example fix

// before
await installSkill({ workspaceId: roWorkspaceId, ... }); // 403
// after
const ws = await getWorkspace(writableId); // filesystem.readOnly === false
await installSkill({ workspaceId: ws.id, ... });
Defensive patterns

Strategy: validation

Validate before calling

const ws = await getWorkspace(workspaceId);
if (ws?.filesystem?.readOnly) throw new Error('Workspace is read-only; select a writable workspace before installing skills.');

Type guard

function isWritableWorkspace(ws: unknown): boolean {
  const f = (ws as any)?.filesystem;
  return !!f && f.readOnly === false;
}

Try / catch

try {
  await installSkill({ workspaceId, ... });
} catch (e) {
  if (/Workspace is read-only/.test(String(e))) {
    throw new Error('Switch to a writable workspace or unlock this one before installing skills.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the install-skill endpoint on a workspace whose filesystem.readOnly flag is true (e.g. snapshot/checked-out read-only checkouts, restricted environments).

Common situations: Workspaces mounted from read-only sources (git checkout, container image layers); intentionally locked workspaces during review; permission changes by an administrator after workspace creation.

Related errors


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