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
- Remove or encode the unsafe characters from the offending argument before calling the setup API.
- Pass each logical argument as a separate token instead of one combined command string (never "plugin add foo && rm -rf x").
- 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.
- 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
- Validate user/plugin/repo inputs against a strict allowlist regex before they reach commands
- Pass one argument per token; never build combined command strings
- On Windows, prefer spawning without shell:true and quote via this library only
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
- File path contains unsafe shell characters
- Unsafe character in Windows linter argument
- artifact must be a resident regular file
- artifact path escapes output directory
- artifact path must be canonical and absolute
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)