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

Unsafe ref name

Error message

Unsafe ref name: ${JSON.stringify(ref)}

What it means

Thrown by quoteCustomRef() when a Git ref (branch/tag name) fails the whitelist regex ^[\w.\-+/]+$. The function exists to safely embed user-supplied or URL-parsed refs into git --branch and git fetch commands, preventing shell injection. Any ref containing spaces, semicolons, pipes, or other shell metacharacters is rejected before reaching execSync.

Solutions

  1. Check the ref string for whitespace or shell metacharacters and remove/escape them before passing as a version suffix.
  2. If the ref is a raw commit SHA, note that git clone --branch cannot use SHAs — use --pin at the module level or a branch/tag name instead.
  3. Ensure options.pinOverride, when provided, is a simple alphanumeric/semver tag string.
  4. URL-encode branch names containing slashes inside the path, or quote the @version suffix properly.

Example fix

// before
const url = 'https://github.com/org/repo.git@feature/fix bug';
await mgr.cloneRepo(url);

// after
const url = 'https://github.com/org/repo.git@feature/fix-bug';
await mgr.cloneRepo(url);
Defensive patterns

Strategy: validation

Validate before calling

function isValidRef(ref) {
  return typeof ref === 'string' && /^[\w.\-+/]+$/.test(ref);
}

// Before calling cloneRepo:
const version = extractVersion(url);
if (version && !isValidRef(version)) {
  throw new Error(`Invalid ref name: ${version}`);
}

Type guard

function isSafeRef(ref) {
  return typeof ref === 'string' && ref.length > 0 && /^[\w.\-+/]+$/.test(ref) && !ref.includes('//');
}

Try / catch

try {
  await mgr.cloneRepo(url, { pinOverride });
} catch (e) {
  if (e.message.startsWith('Unsafe ref name')) {
    console.error('The version/tag contains invalid characters. Use only letters, digits, dots, hyphens, underscores, plus, and slashes.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling CustomModuleManager.cloneRepo() with a source URL whose @version suffix or /tree/<ref> path segment contains characters outside [A-Za-z0-9_.\-+/]. Also triggered when a parsed default branch name from git symbolic-ref contains unexpected characters, or when options.pinOverride is set to a malformed value.

Common situations: A user supplies a URL like https://github.com/org/repo.git@feature/my branch (space in branch name); a ref with a colon like 'HEAD~1'; a non-string pinOverride passed programmatically; a ref derived from a malformed deep-path URL that the parser partially consumed.

Related errors


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

Appendix: source

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

const fs = require('../fs-native');
const os = require('node:os');
const path = require('node:path');
const { execSync } = require('node:child_process');
const prompts = require('../prompts');
const { gitEnv } = require('./git-env');

function quoteCustomRef(ref) {
  if (typeof ref !== 'string' || !/^[\w.\-+/]+$/.test(ref)) {
    throw new Error(`Unsafe ref name: ${JSON.stringify(ref)}`);
  }
  return `"${ref}"`;
}

function isLocalSourcePath(input) {
  return (
    input.startsWith('/') ||
    input.startsWith('./') ||
    input.startsWith('../') ||
    input.startsWith('.\\') ||
    input.startsWith('..\\') ||
    input.startsWith('~') ||
    path.win32.isAbsolute(input)
  );
}

/**
 * Manages custom modules installed from user-provided sources.

View on GitHub (pinned to b70486b9bd)