ruvnet/RuView · error · TypeError

command must be a non-empty string

Error message

command must be a non-empty string

What it means

Thrown synchronously by runProcess() in the Homecore metaharness when the command argument is falsy or not a JavaScript string. The runner spawns children with shell:false, so it must receive an explicit executable name; this guard rejects undefined/null/empty/number inputs before any process is created.

Source

Thrown at harness/homecore/src/process-runner.js:98

      } catch {
        // The process tree already exited.
      }
    }
  }, 2_000);
  force.unref();
  return force;
}

export function runProcess(command, args = [], {
  cwd,
  input = '',
  timeoutMs = 120_000,
  signal,
  maxOutputBytes = 1_048_576,
  env = process.env,
  envAllowlist = DEFAULT_ENV_ALLOWLIST,
} = {}) {
  if (!command || typeof command !== 'string') throw new TypeError('command must be a non-empty string');
  if (!Array.isArray(args) || !args.every((arg) => typeof arg === 'string')) {
    throw new TypeError('args must be an array of strings');
  }
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 1_800_000) {
    throw new RangeError('timeoutMs must be a safe integer between 1000 and 1800000');
  }
  if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1) {
    throw new RangeError('maxOutputBytes must be a positive safe integer');
  }
  const childEnv = scrubEnvironment(env, envAllowlist);

  return new Promise((resolve, reject) => {
    const child = spawn(command, args, {
      cwd,
      env: childEnv,
      detached: process.platform !== 'win32',
      shell: false,
      windowsHide: true,

View on GitHub (pinned to 4685618388)

Solutions

  1. Pass an explicit non-empty executable string, e.g. runProcess('cargo', ['--version'])
  2. Trace where the command value originates and fail early there with better context if it is undefined
  3. If you intended an options-only call, add the command as the first argument

Example fix

// before
const cmd = platformCommands[process.platform];
await runProcess(cmd, ['--version']); // cmd is undefined on an unhandled platform

// after
const cmd = platformCommands[process.platform];
if (typeof cmd !== 'string' || !cmd) throw new Error(`no command configured for ${process.platform}`);
await runProcess(cmd, ['--version']);
Defensive patterns

Strategy: type-guard

Validate before calling

const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;
if (!isNonEmptyString(command)) {
  throw new Error(`runner command missing or not a string: got ${typeof command}`);
}

Type guard

/** @param {unknown} v @returns {v is string} */
function isCommand(v) {
  return typeof v === 'string' && v.length > 0;
}

Try / catch

try {
  await runProcess(command, args);
} catch (error) {
  if (error instanceof TypeError && error.message.includes('command must be a non-empty string')) {
    throw new Error(`runner misconfigured: command resolved to ${JSON.stringify(command)}`);
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling runProcess(''), runProcess(null), runProcess(undefined) (command has no default value), or runProcess(42). Most often the command comes from a config or platform lookup that returned undefined, or the caller passes the options object first, e.g. runProcess({cwd}) assuming an options-first signature.

Common situations: Platform-specific command selection missing a branch (e.g. process.platform key absent), migrating from string-based APIs like child_process.exec('cargo test'), or test stubs that omit the command field.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/892a481a09270df5. Report an issue: GitHub.