affaan-m/ECC · error · Error

Executable must be one argv entry, not an interpolated shell

Error message

Executable must be one argv entry, not an interpolated shell command string.

What it means

validateExecutable computes whether the value 'resembles an executable path' (it is absolute, or a path separator appears before any whitespace). If the value contains whitespace AND does not resemble a path, it is rejected. Rationale: launch always uses shell:false, so a whitespace-laden token is passed as one bogus argv entry rather than parsed as a command. This is the whitespace branch (line 62-65).

Source

Thrown at skills/terminal-opener/scripts/open-terminal.js:63

function validateCwd(value) {
  if (value.includes('\0')) throw new Error('--cwd must not contain a NUL byte.');
  if (!isAbsolutePath(value)) throw new Error('--cwd must be an absolute path.');
}

function validateExecutable(value) {
  if (!value || /[\0\r\n]/.test(value)) {
    throw new Error('Executable must be a non-empty argv entry without control bytes.');
  }

  const whitespaceIndex = value.search(/\s/);
  const separatorIndexes = [value.indexOf('/'), value.indexOf('\\')].filter(index => index >= 0);
  const firstSeparatorIndex = separatorIndexes.length > 0 ? Math.min(...separatorIndexes) : -1;
  const resemblesExecutablePath = isAbsolutePath(value)
    || (firstSeparatorIndex >= 0 && (whitespaceIndex < 0 || firstSeparatorIndex < whitespaceIndex));

  if (whitespaceIndex >= 0 && !resemblesExecutablePath) {
    throw new Error(
      'Executable must be one argv entry, not an interpolated shell command string.'
    );
  }
  if (!resemblesExecutablePath && /[;&|<>`$]/.test(value)) {
    throw new Error(
      'Executable must be one argv entry, not an interpolated shell command string.'
    );
  }
}

function validateArgv(argv) {
  for (const argument of argv) {
    if (argument.includes('\0')) throw new Error('Arguments must not contain NUL bytes.');
  }
}

function readValue(argv, index, option) {
  const value = argv[index + 1];

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Put the executable as the first token after --, and pass each argument as its own token after that.
  2. If you genuinely need a shell command, invoke a shell explicitly: -- /bin/sh -c '<command>' (sh becomes the executable, -c and the string become args).

Example fix

# before
node open-terminal.js -- "bash -c 'echo hi'"

# after
node open-terminal.js -- bash -c "echo hi"
Defensive patterns

Strategy: validation

Validate before calling

function isSingleArgvEntry(executable) {
  if (!executable) return false;
  const sep = [executable.indexOf('/'), executable.indexOf('\\')].filter(i => i >= 0);
  const firstSep = sep.length ? Math.min(...sep) : -1;
  const ws = executable.search(/\s/);
  const pathLike = path.isAbsolute(executable) || (firstSep >= 0 && (ws < 0 || firstSep < ws));
  return !ws || pathLike;
}
if (!isSingleArgvEntry(executable)) {
  throw new Error('Pass executable and args as separate tokens after --');
}

Prevention

When it happens

Trigger: options.executable contains whitespace and is not path-like — e.g. a single token 'bash -c echo hi' passed as the executable. The executable slot should hold one argv entry only.

Common situations: Passing a shell command string instead of separating the executable and its args after --; copy-pasting a command line into the executable argument.

Related errors


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