affaan-m/ECC · error · 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

quoteWindowsCommandToken quotes a single argument for cmd.exe before invoking the Claude Code CLI on Windows. cmd.exe treats characters like & | < > ^ % ! and newlines as metacharacters, so any token containing them is rejected outright rather than escaped, preventing command injection or broken commands.

Solutions

  1. Remove or encode the unsafe characters from the offending argument before calling the setup API.
  2. Pass each logical argument as a separate token instead of one combined command string (never "plugin add foo && rm -rf x").
  3. If a value legitimately contains such characters (e.g. % in a path), rename the file/directory or use an 8.3/alternate path without metacharacters.
  4. Sanitize upstream inputs (validate plugin/repo names against ^[A-Za-z0-9._\/-]+$) before they reach the command builder.

Example fix

// before
const cmd = buildWindowsCommand(['plugin', 'add', userInput]); // userInput = "foo && calc"
// after
if (!/^[\w.\/-]+$/.test(userInput)) throw new Error('Invalid plugin name');
const cmd = buildWindowsCommand(['plugin', 'add', userInput]);
Defensive patterns

Strategy: validation

Validate before calling

const UNSAFE = /[\r\n&|<>^%!]/;
function assertSafeToken(v) { if (UNSAFE.test(String(v))) throw new Error(`Unsafe cmd.exe characters in: ${v}`); }

Type guard

const isShellSafe = (v) => typeof v === 'string' && !/[\r\n&|<>^%!]/.test(v);

Try / catch

try { await setupPlugin({ name }); } catch (e) { if (e.message.includes('unsafe for cmd.exe')) { console.error('Strip shell metacharacters from the argument'); } else throw e; }

Prevention

When it happens

Trigger: Calling the Windows command builder with arguments containing & (e.g. "a && b"), pipes, redirection characters, ^ or % (env expansion), ! (delayed expansion), or embedded \r\n — typically user-controlled values such as repo names, paths, or plugin names flowing into the claude CLI command.

Common situations: Plugin or marketplace names pasted with shell operators; Windows paths accidentally containing stray characters; scripts that build multi-command strings instead of passing one token per argument; CI inputs with untrusted text.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/24338abb231cd2e8. Report an issue: GitHub.

Appendix: source

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

      || 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 8321021c54)