mastra-ai/mastra · error

Directory not found in composite skill source: ${path}

Error message

Directory not found in composite skill source: ${path}

What it means

CompositeVersionedSkillSource.readdir throws this when #routePath cannot resolve the requested directory to any mounted skill version or fallback source. The composite serves the virtual root itself (readdir('') lists all mounted skills), but any subdirectory must live inside a mounted skill tree or the fallback filesystem source.

Source

Thrown at packages/core/src/workspace/skills/composite-versioned-skill-source.ts:188

      for (const dirName of this.#sources.keys()) {
        entries.push({ name: dirName, type: 'directory' });
        seen.add(dirName);
      }

      // Also list fallback skills
      for (const dirName of this.#fallbackSkills) {
        if (!seen.has(dirName)) {
          entries.push({ name: dirName, type: 'directory' });
          seen.add(dirName);
        }
      }

      return entries;
    }

    const route = this.#routePath(path);
    if (!route) {
      throw new Error(`Directory not found in composite skill source: ${path}`);
    }

    return route.source.readdir(route.subPath);
  }

  async realpath(path: string): Promise<string> {
    const normalized = this.#normalizePath(path);
    if (normalized === '') return '';

    const route = this.#routePath(path);
    if (!route) {
      throw new Error(`Path not found in composite skill source: ${path}`);
    }

    const realSubPath = route.source.realpath ? await route.source.realpath(route.subPath) : route.subPath;
    return [route.mountDir, realSubPath].filter(Boolean).join('/');
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. List the mounted skills with readdir('') and use one of the returned directory names as the first path segment.
  2. Confirm the skill's VersionedSkillEntry (dirName/tree) was included when the composite source was constructed.
  3. Ensure a fallback SkillSource is configured if the directory belongs to a live (unpublished) skill.
  4. Guard with exists(path) before calling readdir to convert the failure into a controlled branch.

Example fix

// before
await source.readdir('references'); // no such top-level dir
// after
await source.readdir('brand-guidelines/references');
Defensive patterns

Strategy: validation

Validate before calling

if (!(await source.exists(dir))) {
  return []; // or surface a friendly 'no such directory' to the UI
}
const entries = await source.readdir(dir);

Try / catch

try {
  return await source.readdir(dir);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Directory not found in composite skill source')) {
    return [];
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling readdir with a directory path whose first segment does not match any mounted skill dirName and which is not servable by the configured fallback source; also thrown for paths that normalize away to an unroutable value when no fallback exists.

Common situations: Listing a skill directory that was renamed or republished under a different dirName; scanning '/assets' at the composite root instead of '/<skill>/assets'; stale cached skill names after a republish removed the version from storage.

Related errors


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