mastra-ai/mastra · warning · HTTPException
skillPath does not exist on the server filesystem: ${resolve
Error message
skillPath does not exist on the server filesystem: ${resolvedPath}. Create the skill directory (with a SKILL.md) before publishing, or use a skill that was materialized to disk. What it means
A 400 thrown when `fs.stat(resolvedPath)` fails with ENOENT — the `skillPath` directory does not exist on the SERVER's filesystem. The handler checks up front so callers get a clear 400 instead of a raw 500/ENOENT deeper in the publish flow. Note this checks the server host, so a path valid on a client machine can still be missing on the server.
Source
Thrown at packages/server/src/server/handlers/stored-skills.ts:630
const resolvedPath = path.default.resolve(skillPath);
const allowedBase = path.default.resolve(process.env.SKILLS_BASE_DIR || process.cwd());
if (!resolvedPath.startsWith(allowedBase + path.default.sep) && resolvedPath !== allowedBase) {
throw new HTTPException(400, {
message: `skillPath must be within the allowed directory: ${allowedBase}`,
});
}
// Verify the source directory exists and contains a SKILL.md before attempting
// to publish, so callers get a 400 with context instead of a raw 500/ENOENT.
try {
const stat = await fs.stat(resolvedPath);
if (!stat.isDirectory()) {
throw new HTTPException(400, { message: `skillPath is not a directory: ${resolvedPath}` });
}
} catch (err) {
if (err instanceof HTTPException) throw err;
if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {
throw new HTTPException(400, {
message: `skillPath does not exist on the server filesystem: ${resolvedPath}. Create the skill directory (with a SKILL.md) before publishing, or use a skill that was materialized to disk.`,
});
}
throw err;
}
try {
await fs.stat(path.default.join(resolvedPath, 'SKILL.md'));
} catch (err) {
if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {
throw new HTTPException(400, {
message: `skillPath is missing SKILL.md: ${resolvedPath}`,
});
}
throw err;
}
// Use LocalSkillSource to read from the server filesystem
const source = new LocalSkillSource();View on GitHub (pinned to 75dd419e61)
Solutions
- Create the skill directory (including SKILL.md) on the server before publishing, or use a skill previously materialized to disk
- Mount the skills directory into the container / ensure SKILLS_BASE_DIR points at the mounted volume
- Check existence locally against the server's view: `fs.stat(path.resolve(SKILLS_BASE_DIR, relPath))`
- For remote servers, upload/materialize the skill first (or use an API-supported source) instead of referencing local paths
Example fix
// before
await client.publishStoredSkill({ skillPath: './skills/my-skill' }); // missing on server
// after
// server: mkdir -p /srv/skills/my-skill && create SKILL.md, SKILLS_BASE_DIR=/srv/skills
await client.publishStoredSkill({ skillPath: '/srv/skills/my-skill' }); Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs/promises'; await fs.access(path.resolve(skillPath)); // throws ENOENT locally first; ensure the same path exists on the server
Try / catch
try { await publishStoredSkill({ skillPath }); } catch (e) {
if (e.status === 400 && /does not exist on the server filesystem/.test(e.message)) {
throw new Error('Create the skill directory (with SKILL.md) on the server, or materialize the skill to disk first');
}
throw e;
} Prevention
- Materialize/create the skill directory on the server host before publishing
- Mount the skills volume in containers and align SKILLS_BASE_DIR
- Use absolute server-side paths, not client-relative ones
- Pre-check with a listing endpoint or ssh fs.stat before publishing
When it happens
Trigger: Publishing a skillPath that was never created, was deleted, exists only on the caller's local machine against a remote server, or uses a relative path that resolves differently under the server's cwd.
Common situations: Local dev path sent to a deployed server; typo in the directory name; publishing before materializing a skill to disk; containerized deployments where the path was not volume-mounted.
Related errors
- MASTRA_ENTRY_FILE_NOT_FOUND
- Failed to copy studio assets from "${studioSource}" to "${st
- skillPath is not a directory: ${resolvedPath}
- Mount "${requestedMount}" not found. Available mounts: ${fil
- NODE_FAIL_INSTALL_SPECIFIED_VERSION
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/418d479008c4de4a.
Report an issue: GitHub.