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
- Set a non-empty config.launcherCommand so all workers inherit it, OR set worker.launcherCommand on every worker.
- Validate before calling: const ok = workers.every(w => (w.launcherCommand || config.launcherCommand || '').trim().length > 0);
- Use the worker name in the message to find the offending entry.
- 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
- Set a global config.launcherCommand so workers inherit it by default.
- Only override launcherCommand per worker when the launchers truly differ.
- Validate the workers array against a schema that requires either a global or per-worker launcher.
- Surface the worker name from the message to pinpoint the gap.
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
- launcherCommand must be a non-empty string
- Seed path does not exist in repoRoot: ${seedPath}
- buildOrchestrationPlan requires at least one worker
- Worker ${index + 1} is missing a task
- Invalid mode "${mode}". Allowed modes: ${allowedModes.join('
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/30383caad1d46847.
Report an issue: GitHub.