affaan-m/ECC · error · Error

Script name must be a non-empty string

Error message

Script name must be a non-empty string

What it means

Thrown by getRunCommand() in scripts/lib/package-manager.js when the `script` argument is falsy or not a string. getRunCommand maps a script name (dev/build/test/install or arbitrary) to the detected package manager's run command; this is the first of two guards that prevent shell-metacharacter injection by validating input shape before the command string is built.

Source

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

  } catch (err) {
    throw new Error(`Failed to save package manager config to ${configPath}: ${err.message}`);
  }
  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 01e15490f0)

Solutions

  1. Pass a literal known script name: getRunCommand('test', { projectDir }).
  2. If the script name is dynamic, default-coalesce first: getRunCommand(scriptName || 'build', options).
  3. Validate the upstream source supplies a non-empty string before calling (e.g. guard on typeof + length).
  4. If the input is genuinely optional, branch on its presence and skip the call rather than passing undefined.

Example fix

// before
const cmd = getRunCommand(userScript, opts); // userScript is undefined

// after
if (!userScript || typeof userScript !== 'string') {
  throw new Error('A script name is required');
}
const cmd = getRunCommand(userScript, opts);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!script || typeof script !== 'string') {
  throw new Error('script name is required');
}
const cmd = getRunCommand(script, opts);

Type guard

function isScriptName(value) {
  return typeof value === 'string' && value.length > 0;
}

Prevention

When it happens

Trigger: Calling getRunCommand() with no arguments, getRunCommand(undefined), getRunCommand(''), getRunCommand(null), or getRunCommand(123). Any caller that reads the script name from an untrusted or optional source (CLI flag, config file, object property) and forwards it without a type check hits this.

Common situations: Refactoring a caller so the script variable is conditionally assigned and becomes undefined; reading a script name from a JSON config that omits the key; a wrapper that defaults args to undefined instead of a real script name; test fixtures that pass nothing.

Related errors


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