affaan-m/ECC · error

Script name contains unsafe characters

Error message

Script name contains unsafe characters: ${script}

What it means

getRunCommand() builds the shell command for running a package-manager script (npm/pnpm/yarn/bun run <script>). Before doing anything it validates the script name against SAFE_NAME_REGEX and throws this error when the name contains characters outside the safe allowlist (letters, digits, hyphens, slashes, @, ., etc.). The library throws it to prevent shell metacharacters in caller-supplied names from being injected into a generated command line.

Solutions

  1. Pass only the bare script name ('test', 'build:fast') and move flags/arguments to a separate mechanism (e.g. the runner's argument options or getExecCommand with validated args).
  2. Inspect the offending value printed in the message and remove shell metacharacters; the regex only allows a safe name charset.
  3. If the script lives in a workspace, use the package/workspace selector supported by the detected package manager rather than embedding 'cd x && ...' in the name.
  4. Sanitize or reject upstream input (CLI args, config values) before it reaches getRunCommand; validate with the same allowlist pattern.

Example fix

// before
getRunCommand(`test --grep "${pattern}"`)
// after
getRunCommand('test') // pass pattern via the runner's own arg mechanism
getExecCommand('jest', '--grep', { pattern: 'safe-pattern' })
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_NAME = /^[A-Za-z0-9@/._-]+$/;
if (typeof script !== 'string' || !SAFE_NAME.test(script)) {
  throw new Error(`Invalid script name: ${JSON.stringify(script)}`);
}
getRunCommand(script);

Type guard

function isSafeScriptName(v) {
  return typeof v === 'string' && /^[A-Za-z0-9@/._-]+$/.test(v);
}

Try / catch

try {
  const cmd = getRunCommand(script);
} catch (e) {
  if (String(e.message).startsWith('Script name contains unsafe characters')) {
    console.error(`Rejecting script name: ${script}. Use a bare script name without flags or shell metacharacters.`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getRunCommand(script) where script is a non-empty string that fails SAFE_NAME_REGEX — e.g. containing spaces, semicolons, backticks, '$()', '&&', quotes, or other shell metacharacters. Examples: getRunCommand('test --grep foo'), getRunCommand('build; rm -rf /'), getRunCommand("test'x").

Common situations: Script names built by string concatenation with flags or arguments appended ('test -- --watch' instead of passing watch separately); interpolating user input or CLI arguments into the script name; copy-pasted command lines rather than bare script names; automated tooling that forwards raw terminal strings as the script parameter.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at scripts/lib/package-manager.js:301

  return config;
}

// Allowed characters in script/binary names: alphanumeric, dash, underscore, dot, slash, @
// This prevents shell metacharacter injection while allowing scoped packages (e.g., @scope/pkg)
const SAFE_NAME_REGEX = /^[@a-zA-Z0-9_./-]+$/;

/**
 * Get the command to run a script
 * @param {string} script - Script name (e.g., "dev", "build", "test")
 * @param {object} options - { projectDir }
 * @throws {Error} If script name contains unsafe characters
 */
function getRunCommand(script, options = {}) {
  if (!script || typeof script !== 'string') {
    throw new Error('Script name must be a non-empty string');
  }
  if (!SAFE_NAME_REGEX.test(script)) {
    throw new Error(`Script name contains unsafe characters: ${script}`);
  }

  const pm = getPackageManager(options);

  switch (script) {
    case 'install':
      return pm.config.installCmd;
    case 'test':
      return pm.config.testCmd;
    case 'build':
      return pm.config.buildCmd;
    case 'dev':
      return pm.config.devCmd;
    default:
      return `${pm.config.runCmd} ${script}`;
  }
}

View on GitHub (pinned to 8321021c54)