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
- List the mounted skills with readdir('') and use one of the returned directory names as the first path segment.
- Confirm the skill's VersionedSkillEntry (dirName/tree) was included when the composite source was constructed.
- Ensure a fallback SkillSource is configured if the directory belongs to a live (unpublished) skill.
- 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
- Enumerate mounted skills with readdir('') before descending.
- Treat the composite root as virtual — never expect arbitrary top-level dirs.
- Verify skill dirNames passed to the constructor match the names used at runtime.
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
- File not found in composite skill source: ${path}
- ENOENT: no such file or directory: ${path}
- FilesystemSkillsStorage: skill with id ${id} not found
- Path not found in skill version tree: ${path}
- File not found in skill version tree: ${path}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/74070914deacded9.
Report an issue: GitHub.