affaan-m/ECC · error

Worker ${workerName} is missing a launcherCommand

Error message

Worker ${workerName} is missing a launcherCommand

What it means

Thrown inside the workers.map loop after templateVariables are built, when neither the worker's own launcherCommand nor the global default launcherCommand (config.launcherCommand) is a non-empty string. Up to this point the worker has a task and a unique slug, but there is no command to actually launch inside the tmux pane. Aborting here prevents producing a plan that cannot be executed.

Source

Thrown at scripts/lib/tmux-worktree-orchestrator.js:229

    const handoffFilePath = path.join(workerCoordinationDir, 'handoff.md');
    const statusFilePath = path.join(workerCoordinationDir, 'status.md');
    const launcherCommand = worker.launcherCommand || defaultLauncher;
    const workerSeedPaths = normalizeSeedPaths(worker.seedPaths, repoRoot);
    const seedPaths = normalizeSeedPaths([...globalSeedPaths, ...workerSeedPaths], repoRoot);
    const templateVariables = buildTemplateVariables({
      branch_name: branchName,
      handoff_file: handoffFilePath,
      repo_root: repoRoot,
      session_name: sessionName,
      status_file: statusFilePath,
      task_file: taskFilePath,
      worker_name: workerName,
      worker_slug: workerSlug,
      worktree_path: worktreePath
    });

    if (!launcherCommand) {
      throw new Error(`Worker ${workerName} is missing a launcherCommand`);
    }

    const gitArgs = ['worktree', 'add', '-b', branchName, worktreePath, baseRef];

    return {
      branchName,
      coordinationDir: workerCoordinationDir,
      gitArgs,
      gitCommand: formatCommand('git', gitArgs),
      handoffFilePath,
      launchCommand: renderTemplate(launcherCommand, templateVariables),
      repoRoot,
      sessionName,
      seedPaths,
      statusFilePath,
      task: worker.task.trim(),
      taskFilePath,
      workerName,

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Set a non-empty config.launcherCommand so all workers inherit it, OR set worker.launcherCommand on every worker.
  2. Validate before calling: const ok = workers.every(w => (w.launcherCommand || config.launcherCommand || '').trim().length > 0);
  3. Use the worker name in the message to find the offending entry.
  4. Prefer the global default for a homogeneous fleet; only override per worker when they truly differ.

Example fix

// before
buildOrchestrationPlan({
  repoRoot,
  workers: [
    { name: 'alpha', task: '...', launcherCommand: 'claude "{task_file}"' },
    { name: 'beta',  task: '...' },   // no launcher, no global default
  ],
});
// -> Worker beta is missing a launcherCommand

// after
buildOrchestrationPlan({
  repoRoot,
  launcherCommand: 'claude "{task_file}"',   // global default
  workers: [
    { name: 'alpha', task: '...' },
    { name: 'beta',  task: '...' },
  ],
});
Defensive patterns

Strategy: validation

Validate before calling

function ensureLauncherOnAll(config) {
  const global = (config.launcherCommand || '').trim();
  config.workers.forEach(w => {
    if (!(w.launcherCommand || global).trim()) {
      throw new Error(`Worker ${w.name || '?'} has no launcherCommand`);
    }
  });
}

ensureLauncherOnAll(config);
buildOrchestrationPlan(config);

Type guard

function allWorkersHaveLauncher(config) {
  const global = (config?.launcherCommand || '').trim();
  return config.workers.every(w => Boolean((w.launcherCommand || global).trim()));
}

Try / catch

try {
  buildOrchestrationPlan(config);
} catch (error) {
  if (/is missing a launcherCommand/.test(error.message)) {
    config.launcherCommand = DEFAULT_LAUNCHER;
    buildOrchestrationPlan(config);
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: config.launcherCommand is unset and a worker omits launcherCommand; config.launcherCommand = '' and the worker has no override; only some workers specify launcherCommand and one of them does not; launcherCommand is whitespace-only and the || fallback chain treats it as empty.

Common situations: Mixed config where the global default was removed but per-worker overrides were not added on every entry; a CLI mode that requires explicit per-worker launcherCommand but the user only set some; refactoring splits a single launcher into per-worker launchers and misses one.

Related errors


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