{"record":{"id":"24338abb231cd2e8","repo":"affaan-m/ECC","slug":"claude-code-command-contains-characters-that-are-u","errorCode":null,"errorMessage":"Claude Code command contains characters that are unsafe for cmd.exe","messagePattern":"Claude Code command contains characters that are unsafe for cmd\\.exe","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/claude-plugin-setup.js","lineNumber":140,"sourceCode":"      || typeof marketplace.source !== 'string'\n      || !['github', 'git'].includes(marketplace.source)\n      || !normalizeMarketplaceRepository(marketplace)\n    ) {\n      fail(\n        'INVALID_MARKETPLACE_INVENTORY',\n        'Claude marketplace inventory contains an invalid `ecc` entry'\n      );\n    }\n  }\n  return marketplaces;\n}\n\nconst UNSAFE_WINDOWS_SHELL_CHARS = /[\\r\\n&|<>^%!]/;\n\nfunction quoteWindowsCommandToken(value) {\n  const token = String(value);\n  if (UNSAFE_WINDOWS_SHELL_CHARS.test(token)) {\n    throw new Error('Claude Code command contains characters that are unsafe for cmd.exe');\n  }\n  if (token === '') return '\"\"';\n  if (!/[\\s\"]/.test(token)) return token;\n  return `\"${token.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction buildWindowsCommandLine(command, args) {\n  return [command, ...args].map(quoteWindowsCommandToken).join(' ');\n}\n\nfunction resolveWindowsCmdShim(command, env) {\n  if (typeof command !== 'string' || command.length === 0) return null;\n  if (/\\.(cmd|bat)$/i.test(command)) return command;\n  if (path.extname(command)) return null;\n\n  const isPathLike = path.isAbsolute(command)\n    || command.includes('/')\n    || command.includes('\\\\');","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/claude-plugin-setup.js#L122-L158","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the args being passed to runClaude / spawnClaude and strip or URL-encode any of & | < > ^ % ! CR LF in path and URL tokens.","Avoid percent-encoded characters in the OFFICIAL_MARKETPLACE_URL path on Windows; use a plain ASCII path.","If the character lives in the project root path, run ECC setup from a path without shell metacharacters.","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."],"exampleFix":"// before\nrunClaude(['plugin', 'marketplace', 'add', 'https://github.com/x/y%20z'], { cwd: 'C:\\repos\\a&b' });\n// after — quote-safe path and plain URL\nrunClaude(['plugin', 'marketplace', 'add', 'https://github.com/x/y'], { cwd: 'C:\\\\repos\\\\plain' });","handlingStrategy":"validation","validationCode":"const UNSAFE = /[\\r\\n&|<>^%!]/;\nfunction sanitizeWindowsToken(value) {\n  const s = String(value);\n  if (UNSAFE.test(s)) throw new Error(`token unsafe for cmd.exe: ${JSON.stringify(s)}`);\n  return s;\n}\n// before invoking claude on Windows\n[command, ...args].forEach(sanitizeWindowsToken);","typeGuard":null,"tryCatchPattern":"try {\n  buildWindowsCommandLine(shim, args);\n} catch (err) {\n  if (err.message === 'Claude Code command contains characters that are unsafe for cmd.exe') {\n    // identify the offending token and re-issue without it, or run on a non-Windows host\n  }\n  throw err;\n}","preventionTips":["Avoid '%' in marketplace URLs and paths on Windows.","URL-encode or strip shell metacharacters from any token derived from user input before spawning claude.","Run ECC plugin setup from paths without &, |, !, ^ in any segment."],"tags":["windows","security","shell-injection","cli","claude-plugin","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}