bmad-code-org/BMAD-METHOD · error · Error
Not a valid Git URL or local path
Error message
Not a valid Git URL or local path
What it means
Thrown by resolveSource() when parseSource() cannot match the input as a local path, SSH Git URL (git@host:owner/repo), or HTTP(S) URL. The input falls through all detection patterns and is rejected.
Source
Thrown at tools/installer/modules/custom-module-manager.js:331
if (!Array.isArray(plugins) || plugins.length === 0) {
throw new Error('marketplace.json contains no plugins');
}
return plugins.map((plugin) => this._normalizeCustomModule(plugin, sourceUrl, marketplaceData));
}
// ─── Source Resolution ────────────────────────────────────────────────────
/**
* High-level coordinator: parse input, clone if URL, determine discovery vs direct mode.
* @param {string} input - URL or local path
* @param {Object} [options] - Options passed to cloneRepo
* @returns {Object} { parsed, rootDir, repoPath, sourceUrl, marketplace, mode: 'discovery'|'direct' }
*/
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`);
}
}View on GitHub (pinned to b70486b9bd)
Solutions
- Prefix the input with 'https://' for HTTP(S) Git URLs: 'https://github.com/org/repo'.
- Use SSH format with the git@ prefix: 'git@github.com:org/repo.git'.
- For local paths, ensure the path starts with '/', './', '../', '~', or is a Windows absolute path.
Example fix
// before
await mgr.resolveSource('github.com/org/repo');
// after
await mgr.resolveSource('https://github.com/org/repo'); Defensive patterns
Strategy: validation
Validate before calling
function looksLikeValidSource(input) {
if (!input || typeof input !== 'string') return false;
const trimmed = input.trim();
if (trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../') || trimmed.startsWith('~')) return true;
if (/^https?:\/\//i.test(trimmed)) return true;
if (/^git@[^:]+:.+/.test(trimmed)) return true;
return false;
}
if (!looksLikeValidSource(input)) {
throw new Error('Input must be a Git URL (https://...) or local path');
} Type guard
function isRecognizedSource(input) {
return typeof input === 'string' && (
/^[./~]/.test(input) ||
/^https?:\/\//i.test(input) ||
/^git@[^:]+:/.test(input) ||
require('path').win32.isAbsolute(input)
);
} Try / catch
try {
await mgr.resolveSource(input);
} catch (e) {
if (e.message === 'Not a valid Git URL or local path') {
console.error('Provide a full URL (https://github.com/org/repo) or SSH (git@github.com:org/repo) or local path.');
}
throw e;
} Prevention
- Always prefix URLs with https:// or use the git@ SSH format.
- Avoid shorthand like 'org/repo' — the parser requires a protocol or path indicator.
- Validate the input format before passing to resolveSource.
When it happens
Trigger: Calling resolveSource() with inputs like 'org/repo' (no protocol, no path prefix), 'github.com/org/repo' (no https://), 'ftp://server/repo', or any string that doesn't start with a path indicator or recognized URL scheme.
Common situations: User types a shorthand repo like 'org/repo' instead of a full URL; a copy-paste loses the https:// prefix; an SCP-like syntax without the git@ user prefix; a URL with a typo in the protocol.
Related errors
- Unsafe ref name: ${JSON.stringify(ref)}
- Unsafe ref name: ${JSON.stringify(ref)}
- Source is required
- Local paths do not support @version suffixes
- Path does not exist: ${resolved}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/35ff8562c1c6c28d.
Report an issue: GitHub.