{"record":{"id":"03a649b42447fda6","repo":"mastra-ai/mastra","slug":"workspace-is-read-only","errorCode":null,"errorMessage":"Workspace is read-only","messagePattern":"Workspace is read-only","errorType":"http","errorClass":"HTTPException","httpStatus":403,"severity":"error","filePath":"packages/server/src/server/handlers/workspace.ts","lineNumber":1434,"sourceCode":"  responseSchema: skillsShInstallResponseSchema,\n  summary: 'Install skill from Skills API',\n  description: 'Installs a skill by fetching files from the Skills API and writing to workspace filesystem.',\n  tags: ['Workspace', 'Skills'],\n  handler: async ({ mastra, workspaceId, owner, repo, skillName, mount }) => {\n    try {\n      requireWorkspaceV1Support();\n\n      const workspace = await getWorkspaceById(mastra, workspaceId);\n      if (!workspace) {\n        throw new HTTPException(404, { message: 'Workspace not found' });\n      }\n\n      if (!workspace.filesystem) {\n        throw new HTTPException(400, { message: 'Workspace filesystem not available' });\n      }\n\n      if (workspace.filesystem.readOnly) {\n        throw new HTTPException(403, { message: 'Workspace is read-only' });\n      }\n\n      // Fetch skill files from the Skills API\n      const result = await fetchSkillFiles(owner, repo, skillName);\n      if (!result || result.files.length === 0) {\n        throw new HTTPException(404, {\n          message: `Could not find skill \"${skillName}\" in ${owner}/${repo}.`,\n        });\n      }\n\n      // Validate skill name to prevent path traversal\n      const safeSkillId = assertSafeSkillName(result.skillId);\n      const installPath = buildSkillInstallPath(workspace.filesystem, safeSkillId, mount);\n\n      // Ensure the skills directory exists\n      try {\n        await workspace.filesystem.mkdir(installPath, { recursive: true });\n      } catch {","sourceCodeStart":1416,"sourceCodeEnd":1452,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/workspace.ts#L1416-L1452","documentation":"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.","triggerScenarios":"Calling the install-skill endpoint on a workspace whose filesystem.readOnly flag is true (e.g. snapshot/checked-out read-only checkouts, restricted environments).","commonSituations":"Workspaces mounted from read-only sources (git checkout, container image layers); intentionally locked workspaces during review; permission changes by an administrator after workspace creation.","solutions":["Use a writable workspace for skill installation.","Change the workspace's readOnly flag / mount it read-write if policy allows, then retry.","Write the skill files through an admin or privileged flow that has write access to the underlying storage."],"exampleFix":"// before\nawait installSkill({ workspaceId: roWorkspaceId, ... }); // 403\n// after\nconst ws = await getWorkspace(writableId); // filesystem.readOnly === false\nawait installSkill({ workspaceId: ws.id, ... });","handlingStrategy":"validation","validationCode":"const ws = await getWorkspace(workspaceId);\nif (ws?.filesystem?.readOnly) throw new Error('Workspace is read-only; select a writable workspace before installing skills.');","typeGuard":"function isWritableWorkspace(ws: unknown): boolean {\n  const f = (ws as any)?.filesystem;\n  return !!f && f.readOnly === false;\n}","tryCatchPattern":"try {\n  await installSkill({ workspaceId, ... });\n} catch (e) {\n  if (/Workspace is read-only/.test(String(e))) {\n    throw new Error('Switch to a writable workspace or unlock this one before installing skills.');\n  }\n  throw e;\n}","preventionTips":["Check filesystem.readOnly in the UI and disable install actions for read-only workspaces.","Mount workspaces read-write when they are intended for skill installation.","Re-check readOnly status after permission changes or workspace re-provisioning."],"tags":["http-403","forbidden","read-only","workspace","permissions"],"backgroundTag":"read-only-filesystem","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}