mastra-ai/mastra · error

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

Error message

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

What it means

CompositeVersionedSkillSource.stat routes a virtual path to the underlying per-version skill source via #routePath. When the path does not match any registered version prefix (e.g. a version directory that does not exist), it throws this error instead of delegating. It indicates the requested path is not part of the composite skill's versioned namespace.

Source

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

  }

  async stat(path: string): Promise<SkillSourceStat> {
    const normalized = this.#normalizePath(path);

    // Root directory
    if (normalized === '') {
      return {
        name: '.',
        type: 'directory',
        size: 0,
        createdAt: this.#maxVersionCreatedAt,
        modifiedAt: this.#maxVersionCreatedAt,
      };
    }

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

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

  async readFile(path: string): Promise<string | Buffer> {
    const route = this.#routePath(path);
    if (!route) {
      throw new Error(`File not found in composite skill source: ${path}`);
    }

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

  async readdir(path: string): Promise<SkillSourceEntry[]> {
    const normalized = this.#normalizePath(path);

    // Root: list all mounted skill directories

View on GitHub (pinned to 75dd419e61)

Solutions

  1. List available paths first (e.g. via list/read of the composite source root) and use an exact existing version prefix in the path.
  2. Normalize the version segment against registered versions before calling stat (resolve 'latest' or partial versions to a concrete registered version).
  3. Refresh any cached version listings after skill versions are added/removed, and retry with the corrected path.

Example fix

// before
await source.stat('v3/skill.md'); // v3 not registered -> throws
// after
const entries = await source.list('/');
const path = entries.some(e => e.path === 'v3') ? 'v3/skill.md' : `${entries[0].path}/skill.md`;
await source.stat(path);
Defensive patterns

Strategy: try-catch

Validate before calling

async function safeStat(source, path) {
  const entries = await source.list('/');
  if (!entries.some(e => path === e.path || path.startsWith(e.path + '/'))) {
    throw new Error(`Path ${path} not in registered versions: ${entries.map(e => e.path).join(', ')}`);
  }
  return source.stat(path);
}

Try / catch

try {
  return await source.stat(path);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Path not found in composite skill source')) {
    const versions = await source.list('/');
    console.error(`Unknown path ${path}; available: ${versions.map(v => v.path).join(', ')}`);
    // resolve to a registered version or surface a user-facing not-found
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling stat()/readFile() with a path whose top-level version segment is not a registered version (e.g. 'v99/file.md' when only v1/v2 exist); a bare path without a version prefix that no route accepts; stale version references after a skill version was removed or renamed.

Common situations: Hardcoded version strings in tooling after versions were pruned; listing paths from one source and stat'ing them against another (different) composite source; typos in version folder names ('v1' vs '1.0'); automation reading latest-version paths cached before a re-index.

Related errors


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