bmad-code-org/BMAD-METHOD · error · Error
Unsafe ref name: ${JSON.stringify(ref)}
Error message
Unsafe ref name: ${JSON.stringify(ref)} What it means
Thrown by quoteShell() in external-manager.js when a Git tag/ref passed to git commands fails the whitelist regex ^[\w.\-+/]+$. This is the shell injection guard for external module refs (tags resolved from the GitHub API or user-supplied --pin values). It mirrors quoteCustomRef but operates on the external module code path.
Source
Thrown at tools/installer/modules/external-manager.js:27
const { decideChannelForModule } = require('./channel-plan');
const { getProjectRoot } = require('../project-root');
const VALID_CHANNELS = new Set(['stable', 'next', 'pinned']);
function normalizeChannelName(raw) {
if (typeof raw !== 'string') return null;
const lower = raw.trim().toLowerCase();
return VALID_CHANNELS.has(lower) ? lower : null;
}
/**
* Conservative quoting for tag names passed to git commands. Tags are
* user-typed (--pin) or come from the GitHub API. Only allow the semver
* character class we use to tag BMad releases; anything else throws.
*/
function quoteShell(ref) {
if (typeof ref !== 'string' || !/^[\w.\-+/]+$/.test(ref)) {
throw new Error(`Unsafe ref name: ${JSON.stringify(ref)}`);
}
return `"${ref}"`;
}
async function readChannelMarker(markerPath) {
try {
if (!(await fs.pathExists(markerPath))) return null;
const content = await fs.readFile(markerPath, 'utf8');
return JSON.parse(content);
} catch {
return null;
}
}
async function writeChannelMarker(markerPath, data) {
try {
await fs.writeFile(markerPath, JSON.stringify({ ...data, writtenAt: new Date().toISOString() }, null, 2));
} catch {View on GitHub (pinned to b70486b9bd)
Solutions
- Check the tag name for unexpected characters and use a clean semver tag.
- If the tag comes from the GitHub API, verify the repo's tags on the web UI.
- Ensure --pin values are simple alphanumeric/semver strings.
- Report upstream if a legitimately tagged release has an unusual name.
Example fix
// before // --pin mymodule=my tag with spaces // after // --pin mymodule=v1.2.3
Defensive patterns
Strategy: validation
Validate before calling
function isValidExternalRef(ref) {
return typeof ref === 'string' && /^[\w.\-+/]+$/.test(ref);
}
// Before passing --pin values to external module install:
const pinTag = options.pin;
if (pinTag && !isValidExternalRef(pinTag)) {
throw new Error(`Invalid tag name for --pin: ${pinTag}`);
} Type guard
function isSafeExternalRef(ref) {
return typeof ref === 'string' && ref.length > 0 && /^[\w.\-+/]+$/.test(ref);
} Try / catch
try {
await extMgr.cloneExternalModule(code, options);
} catch (e) {
if (e.message.startsWith('Unsafe ref name')) {
console.error('The tag/ref contains invalid characters. Only letters, digits, dots, hyphens, underscores, plus, and slashes are allowed.');
}
throw e;
} Prevention
- Validate --pin tag values against ^[\w.\-+/]+$ at the CLI boundary.
- Use semver-formatted tags (v1.2.3) which always pass the whitelist.
- Report tags with unusual characters to the upstream module maintainer.
When it happens
Trigger: The GitHub tags API returns a tag name with unexpected characters; a user passes --pin with a malformed value; resolved.ref from resolveChannel() contains characters outside the allowed set. The ref is used in 'git clone --branch' and 'git fetch ... tag' commands.
Common situations: A tag name contains characters like spaces, parentheses, or colons (rare but possible on non-standard repos); the channel resolver returned a ref with a newline or null byte from a malformed API response; a user typos the --pin value with shell metacharacters.
Related errors
- Unsafe ref name: ${JSON.stringify(ref)}
- Not a valid Git URL or local path
- External module '${moduleCode}' not found in the BMad regist
- Tag '${planEntry.pin}' not found in ${parsed.owner}/${parsed
- Failed to clone external module '${moduleCode}' at ${resolve
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/aad143c16389cb81.
Report an issue: GitHub.