affaan-m/ECC · error

Claude Code command contains characters that are unsafe for

Error message

Claude Code command contains characters that are unsafe for cmd.exe

What it means

Thrown by quoteWindowsCommandToken at scripts/lib/claude-plugin-setup.js:139-141 when a token in a Claude Code command line contains any character matched by UNSAFE_WINDOWS_SHELL_CHARS = /[\r\n&|<>^%!]/. The function is invoked from buildWindowsCommandLine, which is itself called from resolveWindowsCmdShim's spawn path when the original spawn failed and a .cmd/.bat shim must be invoked through cmd.exe (claude-plugin-setup.js:191-208). Because the spawn falls back to shell:true, ECC refuses any token that could break cmd.exe quoting or start a new command — this is an injection guard, not a formatting preference. The error is fatal: runClaude wraps it into a CLAUDE_COMMAND_FAILED ClaudeSetupError at claude-plugin-setup.js:197-203.

Source

Thrown at scripts/lib/claude-plugin-setup.js:140

      || typeof marketplace.source !== 'string'
      || !['github', 'git'].includes(marketplace.source)
      || !normalizeMarketplaceRepository(marketplace)
    ) {
      fail(
        'INVALID_MARKETPLACE_INVENTORY',
        'Claude marketplace inventory contains an invalid `ecc` entry'
      );
    }
  }
  return marketplaces;
}

const UNSAFE_WINDOWS_SHELL_CHARS = /[\r\n&|<>^%!]/;

function quoteWindowsCommandToken(value) {
  const token = String(value);
  if (UNSAFE_WINDOWS_SHELL_CHARS.test(token)) {
    throw new Error('Claude Code command contains characters that are unsafe for cmd.exe');
  }
  if (token === '') return '""';
  if (!/[\s"]/.test(token)) return token;
  return `"${token.replace(/"/g, '""')}"`;
}

function buildWindowsCommandLine(command, args) {
  return [command, ...args].map(quoteWindowsCommandToken).join(' ');
}

function resolveWindowsCmdShim(command, env) {
  if (typeof command !== 'string' || command.length === 0) return null;
  if (/\.(cmd|bat)$/i.test(command)) return command;
  if (path.extname(command)) return null;

  const isPathLike = path.isAbsolute(command)
    || command.includes('/')
    || command.includes('\\');

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Inspect the args being passed to runClaude / spawnClaude and strip or URL-encode any of & | < > ^ % ! CR LF in path and URL tokens.
  2. Avoid percent-encoded characters in the OFFICIAL_MARKETPLACE_URL path on Windows; use a plain ASCII path.
  3. If the character lives in the project root path, run ECC setup from a path without shell metacharacters.
  4. On non-Windows hosts this guard never runs (resolveWindowsCmdShim returns null), so consider running setup under WSL or Git Bash with the .cmd shim disabled.

Example fix

// before
runClaude(['plugin', 'marketplace', 'add', 'https://github.com/x/y%20z'], { cwd: 'C:\repos\a&b' });
// after — quote-safe path and plain URL
runClaude(['plugin', 'marketplace', 'add', 'https://github.com/x/y'], { cwd: 'C:\\repos\\plain' });
Defensive patterns

Strategy: validation

Validate before calling

const UNSAFE = /[\r\n&|<>^%!]/;
function sanitizeWindowsToken(value) {
  const s = String(value);
  if (UNSAFE.test(s)) throw new Error(`token unsafe for cmd.exe: ${JSON.stringify(s)}`);
  return s;
}
// before invoking claude on Windows
[command, ...args].forEach(sanitizeWindowsToken);

Try / catch

try {
  buildWindowsCommandLine(shim, args);
} catch (err) {
  if (err.message === 'Claude Code command contains characters that are unsafe for cmd.exe') {
    // identify the offending token and re-issue without it, or run on a non-Windows host
  }
  throw err;
}

Prevention

When it happens

Trigger: On Windows (process.platform === 'win32'), when a `claude` invocation fails (e.g. ENOENT on the bare 'claude' name), resolveWindowsCmdShim finds a `claude.cmd`, and buildWindowsCommandLine is asked to quote an arg containing & | < > ^ % ! or a CR/LF. Examples: a marketplace URL with a '%' (used in cmd variable expansion), an arg containing '&&', or an arg with an embedded newline.

Common situations: A plugin marketplace URL or repo path containing '%' (common in percent-encoded URLs passed unquoted); a project root path with a '!' (bash history expansion residue copied into a Windows path); a Claude config value containing '&' or '|'. The error surfaces during ECC setup on Windows when the Claude Code .cmd shim path is exercised.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/24338abb231cd2e8. Report an issue: GitHub.