mastra-ai/mastra · error · HTTPException

Skill "${skillName}" not found at ${skillPath}

Error message

Skill "${skillName}" not found at ${skillPath}

What it means

HTTP 404 thrown when the skill directory cannot be `stat`-ed at the computed path. The handler first looks up the skill in the skills cache (only if its path is under the skills.sh prefix), otherwise falls back to buildSkillInstallPath; if neither location exists on the filesystem, it reports the skill as not found and includes the exact path checked.

Source

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

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

      // Validate skill name to prevent path traversal
      const safeSkillName = assertSafeSkillName(skillName);

      // Look up the skill's actual path from the cache (supports glob-discovered skills).
      // Only use the discovered path if it's under the skills.sh directory to avoid
      // accidentally deleting a locally-authored skill with the same name.
      const allSkills = await workspace.skills?.list();
      const matchingSkill = allSkills?.find(s => s.name === safeSkillName && s.path.includes(SKILLS_SH_PATH_PREFIX));
      const skillPath = matchingSkill?.path ?? buildSkillInstallPath(workspace.filesystem, safeSkillName);

      // Check if skill exists on filesystem
      try {
        await workspace.filesystem.stat(skillPath);
      } catch {
        throw new HTTPException(404, { message: `Skill "${skillName}" not found at ${skillPath}` });
      }

      // Delete the skill directory
      await workspace.filesystem.rmdir(skillPath, { recursive: true });

      // Surgically remove the skill from the cache
      if (workspace.skills?.removeSkill) {
        try {
          await workspace.skills.removeSkill(skillPath);
        } catch (cacheError) {
          console.warn(
            `[skills-sh] Failed to update cache after remove: ${cacheError instanceof Error ? cacheError.message : String(cacheError)}`,
          );
        }
      }

      return {
        success: true,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. List the workspace skills first and confirm the exact skill name/path before removing.
  2. Correct the skillName spelling/casing to match the installed directory.
  3. If the skill is stale state (already deleted), treat HTTP 404 as idempotent success in your caller.
  4. If installed at a custom path, remove it via the generic filesystem API instead of the skills-sh remove endpoint.

Example fix

// before
await ws.skillsSh.remove({ skillName: 'PdfTools' }); // 404

// after
const skills = await ws.skills.list();
const target = skills.find(s => s.name.toLowerCase() === 'pdftools');
if (target) await ws.skillsSh.remove({ skillName: target.name });
Defensive patterns

Strategy: validation

Validate before calling

const skills = (await workspace.skills?.list()) ?? [];
const target = skills.find(s => s.name === skillName);
if (!target) throw new Error(`Skill "${skillName}" is not installed in workspace ${workspaceId}`);

Type guard

function isInstalledSkill(skills: Array<{ name: string; path: string }>, name: string) {
  return skills.find(s => s.name === name) ?? null;
}

Try / catch

try {
  await removeSkill(workspaceId, skillName);
} catch (e) {
  if (isHttpException(e, 404) && e.message.includes('not found at')) {
    return { removed: false, reason: 'already-absent' }; // treat as idempotent success
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /workspaces/:workspaceId/skills-sh/remove with a skillName that was never installed, was already removed, has a name differing in case/spelling from the installed directory, or was installed outside the skills.sh prefix and at a non-default path.

Common situations: Retrying a remove after a successful delete (double-click, replayed request); typos in skill name; skill installed manually under a custom directory so cache discovery misses it; workspace filesystem reset between install and remove.

Related errors


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