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 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.

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)

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.