bmad-code-org/BMAD-METHOD · error · Error

Subdirectory '${parsed.subdir}' not found in cloned reposito

Error message

Subdirectory '${parsed.subdir}' not found in cloned repository

What it means

Thrown by resolveSource() after a successful clone when the URL contained a subdirectory reference (via /tree/<ref>/<subdir> or ?path=/subdir) but that subdirectory does not exist inside the cloned repository. The check uses fs.pathExists on path.join(repoPath, subdir).

Source

Thrown at tools/installer/modules/custom-module-manager.js:347

  async resolveSource(input, options = {}) {
    const parsed = this.parseSource(input);
    if (!parsed.isValid) throw new Error(parsed.error);

    let rootDir;
    let repoPath;
    let sourceUrl;

    if (parsed.type === 'local') {
      rootDir = parsed.localPath;
      repoPath = null;
      sourceUrl = null;
    } else {
      repoPath = await this.cloneRepo(input, options);
      sourceUrl = parsed.cloneUrl;
      rootDir = parsed.subdir ? path.join(repoPath, parsed.subdir) : repoPath;

      if (parsed.subdir && !(await fs.pathExists(rootDir))) {
        throw new Error(`Subdirectory '${parsed.subdir}' not found in cloned repository`);
      }
    }

    const marketplace = await this.readMarketplaceJsonFromDisk(rootDir);
    const mode = marketplace ? 'discovery' : 'direct';

    return { parsed, rootDir, repoPath, sourceUrl, marketplace, mode };
  }

  // ─── Clone ────────────────────────────────────────────────────────────────

  /**
   * Get the cache directory for custom modules.
   * @returns {string} Path to the custom modules cache directory
   */
  getCacheDir() {
    return path.join(os.homedir(), '.bmad', 'cache', 'custom-modules');
  }

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Open the repository in a browser at the cloned ref and verify the subdirectory path exists.
  2. Remove the subdirectory from the URL to clone the whole repo, then navigate to the correct path.
  3. Pin to a version/tag where the subdirectory is known to exist.

Example fix

// before
await mgr.resolveSource('https://github.com/org/repo/tree/main/src/old-folder');

// after
await mgr.resolveSource('https://github.com/org/repo/tree/main/src/new-folder');
// or clone entire repo:
await mgr.resolveSource('https://github.com/org/repo');
Defensive patterns

Strategy: validation

Validate before calling

// After cloning, verify subdir exists before relying on it
const parsed = mgr.parseSource(url);
if (parsed.subdir) {
  // You can't check before clone, but you can pre-validate by listing remote refs
  console.log(`Will expect subdirectory: ${parsed.subdir}`);
  // After resolveSource succeeds, the subdir is guaranteed to exist
}

Try / catch

try {
  const result = await mgr.resolveSource(url);
} catch (e) {
  if (e.message.includes('not found in cloned repository')) {
    // Retry without the subdirectory to clone the whole repo
    const urlWithoutSubdir = url.replace(/\/tree\/[^/]+\/.*$/, '');
    console.log(`Retrying full repo clone: ${urlWithoutSubdir}`);
    await mgr.resolveSource(urlWithoutSubdir);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Cloning a URL like https://github.com/org/repo/tree/main/src/old-folder where 'src/old-folder' was deleted or renamed after the referenced ref. Also when a deep-path URL points at a path that only exists on a different branch.

Common situations: The repository was restructured and the subdirectory moved or removed; the user copied a deep-link URL from a different branch; the /tree/ path segment was misinterpreted during parsing.

Related errors


AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13). Data as JSON: /api/errors/5272c575933145d5. Report an issue: GitHub.