affaan-m/ECC · error · Error
Worker is missing a task
Error message
Worker ${index + 1} is missing a task What it means
While mapping workers into plans, buildOrchestrationPlan validates that each worker has a non-empty task string and throws with the worker's 1-based index when it does not. This ensures every generated worktree session has real work assigned.
Solutions
- Set a non-empty task string on the worker named in the error message (index is 1-based).
- Rename the wrong field (e.g. description -> task) in the worker config.
- Validate the workers array at config load: every entry has typeof task === 'string' && task.trim().length > 0.
- Fix the producer that generated the worker list to always emit task.
Example fix
// before
workers: [{ name: 'worker-1', description: 'run tests' }]
// after
workers: [{ name: 'worker-1', task: 'run tests' }] Defensive patterns
Strategy: validation
Validate before calling
config.workers.forEach((w, i) => {
if (!w || typeof w.task !== 'string' || w.task.trim().length === 0) {
throw new Error(`Worker ${i + 1} has no task`);
}
}); Type guard
const hasTask = (w) => w != null && typeof w.task === 'string' && w.task.trim().length > 0;
Try / catch
try {
const plan = buildOrchestrationPlan(config);
} catch (e) {
const m = String(e.message).match(/Worker (\d+) is missing a task/);
if (m) console.error(`Fix worker #${m[1]} in config.workers`);
throw e;
} Prevention
- Use the field name 'task' consistently in worker definitions.
- Validate the workers array shape when the config is loaded.
- Avoid deserializing worker lists without a schema check.
- Require tasks in the config schema (min length 1 string).
When it happens
Trigger: Passing workers: [{}] or [{ task: '' }]; a worker object where task is null/undefined or a non-string (number, object); programmatically building workers where the task field is assigned the wrong key name.
Common situations: Config workers entries with 'description' instead of 'task'; deserialized JSON where tasks were dropped; iterating over input files and creating a worker placeholder before the task text is known.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- buildOrchestrationPlan requires at least one worker
- launcherCommand must be a non-empty string
- orchestration template
- seedPaths entries must stay inside repoRoot
- -32602
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/b9524fc6697662db.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/tmux-worktree-orchestrator.js:196
const workers = Array.isArray(config.workers) ? config.workers : [];
const globalSeedPaths = normalizeSeedPaths(config.seedPaths, repoRoot);
const sessionName = slugify(config.sessionName || repoName, 'session');
const worktreeRoot = path.resolve(config.worktreeRoot || path.dirname(repoRoot));
const coordinationRoot = path.resolve(
config.coordinationRoot || path.join(repoRoot, '.orchestration')
);
const coordinationDir = path.join(coordinationRoot, sessionName);
const baseRef = config.baseRef || 'HEAD';
const defaultLauncher = config.launcherCommand || '';
if (workers.length === 0) {
throw new Error('buildOrchestrationPlan requires at least one worker');
}
const seenSlugs = new Set();
const workerPlans = workers.map((worker, index) => {
if (!worker || typeof worker.task !== 'string' || worker.task.trim().length === 0) {
throw new Error(`Worker ${index + 1} is missing a task`);
}
const workerName = worker.name || `worker-${index + 1}`;
const workerSlug = slugify(workerName, `worker-${index + 1}`);
if (seenSlugs.has(workerSlug)) {
throw new Error(`Workers must have unique slugs — duplicate: ${workerSlug}`);
}
seenSlugs.add(workerSlug);
const branchName = `orchestrator-${sessionName}-${workerSlug}`;
const worktreePath = path.join(worktreeRoot, `${repoName}-${sessionName}-${workerSlug}`);
const workerCoordinationDir = path.join(coordinationDir, workerSlug);
const taskFilePath = path.join(workerCoordinationDir, 'task.md');
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);View on GitHub (pinned to 8321021c54)