{"record":{"id":"220e545ad1c99137","repo":"mastra-ai/mastra","slug":"skillpath-is-not-a-directory-resolvedpath","errorCode":null,"errorMessage":"skillPath is not a directory: ${resolvedPath}","messagePattern":"skillPath is not a directory: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"packages/server/src/server/handlers/stored-skills.ts","lineNumber":625,"sourceCode":"      });\n\n      // Validate skillPath to prevent path traversal\n      const path = await import('node:path');\n      const fs = await import('node:fs/promises');\n      const resolvedPath = path.default.resolve(skillPath);\n      const allowedBase = path.default.resolve(process.env.SKILLS_BASE_DIR || process.cwd());\n      if (!resolvedPath.startsWith(allowedBase + path.default.sep) && resolvedPath !== allowedBase) {\n        throw new HTTPException(400, {\n          message: `skillPath must be within the allowed directory: ${allowedBase}`,\n        });\n      }\n\n      // Verify the source directory exists and contains a SKILL.md before attempting\n      // to publish, so callers get a 400 with context instead of a raw 500/ENOENT.\n      try {\n        const stat = await fs.stat(resolvedPath);\n        if (!stat.isDirectory()) {\n          throw new HTTPException(400, { message: `skillPath is not a directory: ${resolvedPath}` });\n        }\n      } catch (err) {\n        if (err instanceof HTTPException) throw err;\n        if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {\n          throw new HTTPException(400, {\n            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.`,\n          });\n        }\n        throw err;\n      }\n      try {\n        await fs.stat(path.default.join(resolvedPath, 'SKILL.md'));\n      } catch (err) {\n        if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {\n          throw new HTTPException(400, {\n            message: `skillPath is missing SKILL.md: ${resolvedPath}`,\n          });\n        }","sourceCodeStart":607,"sourceCodeEnd":643,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/stored-skills.ts#L607-L643","documentation":"A 400 thrown after the traversal check: `fs.stat(resolvedPath)` succeeded but `stat.isDirectory()` is false, so `skillPath` points at a file (often a SKILL.md itself) rather than a skill directory. The publisher expects a directory containing SKILL.md.","triggerScenarios":"Passing `/path/to/my-skill/SKILL.md` or any regular file as `skillPath` in the publish request.","commonSituations":"Copying the file path instead of the folder path from an editor; shell autocomplete picking the file inside the directory; scripts joining the path with 'SKILL.md' twice.","solutions":["Pass the skill's directory path, not the SKILL.md file path","Strip a trailing `/SKILL.md` from the path before calling","Verify locally: `(await fs.stat(p)).isDirectory()` before publishing"],"exampleFix":"// before\nawait publishSkill({ skillPath: '/srv/skills/my-skill/SKILL.md' });\n// after\nawait publishSkill({ skillPath: '/srv/skills/my-skill' });","handlingStrategy":"validation","validationCode":"import fs from 'node:fs/promises';\nconst stat = await fs.stat(skillPath);\nif (!stat.isDirectory()) throw new Error('skillPath must be the skill directory, not SKILL.md or another file');","typeGuard":null,"tryCatchPattern":"try { await publishStoredSkill({ skillPath }); } catch (e) {\n  if (e.status === 400 && /is not a directory/.test(e.message)) throw new Error('Pass the directory containing SKILL.md, not a file path');\n  throw e;\n}","preventionTips":["Pass the folder path, never SKILL.md itself","Stat the path and check isDirectory() before calling the API","Avoid trailing-path joins that append SKILL.md twice","Use directory pickers (not file pickers) in tooling that fills skillPath"],"tags":["validation","filesystem","bad-request"],"backgroundTag":"invalid-filesystem-path","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}