mastra-ai/mastra · error · HTTPException
Mount "${requestedMount}" is read-only
Error message
Mount "${requestedMount}" is read-only What it means
Thrown by buildSkillInstallPath when the requested mount exists in the CompositeFilesystem but is flagged readOnly. Skill installation writes files, so the server refuses with HTTP 403 rather than writing to a read-only mount. This is an intentional safety guard, not a transient failure.
Source
Thrown at packages/server/src/server/handlers/workspace.ts:215
* For non-composite: returns `.agents/skills/<skillId>` (unchanged behavior).
*/
/** Strip a single trailing slash (leaves `/` alone). */
function stripTrailingSlash(p: string): string {
return p.length > 1 && p.endsWith('/') ? p.slice(0, -1) : p;
}
function buildSkillInstallPath(filesystem: WorkspaceFilesystem, safeSkillId: string, requestedMount?: string): string {
if (isCompositeFilesystem(filesystem)) {
if (requestedMount) {
// Validate the requested mount exists
const mountFs = filesystem.mounts.get(requestedMount);
if (!mountFs) {
throw new HTTPException(400, {
message: `Mount "${requestedMount}" not found. Available mounts: ${filesystem.mountPaths.join(', ')}`,
});
}
if (mountFs.readOnly) {
throw new HTTPException(403, { message: `Mount "${requestedMount}" is read-only` });
}
return `${stripTrailingSlash(requestedMount)}/${SKILLS_SH_DIR}/${safeSkillId}`;
}
// Default: use first writable mount
for (const [mountPath, mountFs] of filesystem.mounts) {
if (!mountFs.readOnly) {
return `${stripTrailingSlash(mountPath)}/${SKILLS_SH_DIR}/${safeSkillId}`;
}
}
throw new HTTPException(403, { message: 'No writable mount available for skill installation' });
}
// Non-composite: standard path
return `${SKILLS_SH_DIR}/${safeSkillId}`;
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Retry against a writable mount from the workspace's mount list (omit the mount parameter to auto-select the first writable mount).
- If the mount should be writable, set readOnly: false for that mount in the CompositeFilesystem configuration and redeploy.
- If immutability is intended, change the install pipeline to a mount that is designated for skill writes.
Example fix
new CompositeFilesystem({
mounts: {
'/workspace': createFileSystem({ readOnly: false }), // before: readOnly: true
},
}) Defensive patterns
Strategy: validation
Validate before calling
const mountInfo = await getWorkspaceMountInfo(workspaceId); // [{path, readOnly}]
const target = mountInfo.find(m => m.path === mount);
if (target?.readOnly) {
throw new Error(`Mount "${mount}" is read-only; pick a writable mount`);
} Type guard
function isWritableMount(m: { path: string; readOnly: boolean } | undefined): m is { path: string; readOnly: false } {
return !!m && m.readOnly === false;
} Try / catch
try {
await installSkill({ workspaceId, skillId, mount });
} catch (e) {
if (isHttpException(e, 403) && e.message.includes('is read-only')) {
return installSkill({ workspaceId, skillId }); // auto-select first writable mount
}
throw e;
} Prevention
- Check the mount's readOnly flag before any write or install operation.
- Designate one mount per workspace as the writable target for installs.
- Treat 403 read-only errors as config issues, not retryable failures.
When it happens
Trigger: Calling the skill install/skillPath endpoints with a mount parameter that maps to a mount configured with readOnly: true in the CompositeFilesystem, or the mount's readOnly flag changed (e.g. after a safety-config update) while clients still target it.
Common situations: Deploying a workspace with mounts locked to read-only for production safety while CI pipelines still install skills to those mounts; sharing one workspace config where one team's mount is intentionally immutable.
Related errors
- No writable mount available for skill installation
- EACCES
- No mount for path: ${path}
- READ_ONLY
- READ_ONLY
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1b59d8597801e59b.
Report an issue: GitHub.