affaan-m/ECC · error · Error
Workers must have unique slugs — duplicate
Error message
Workers must have unique slugs — duplicate: ${workerSlug} What it means
buildOrchestrationPlan validates that every worker in an orchestration plan maps to a distinct slug (derived via slugify from worker.name or a positional fallback). If two workers slugify to the same value, later resources (branch, worktree path, coordination dir) would collide, so the plan is rejected up front with this error.
Solutions
- Rename the duplicate workers so each has a unique name in the orchestration config.
- If workers are generated programmatically, append a unique suffix (e.g. index) to each name.
- Check slugify behavior: names differing only in case, punctuation, or whitespace can collapse to the same slug — make names differ alphanumerically.
Example fix
// before
workers: [
{ name: 'Build' },
{ name: 'Build' }
]
// after
workers: [
{ name: 'Build' },
{ name: 'Test' }
] Defensive patterns
Strategy: validation
Validate before calling
const names = workers.map(w => w.name);
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
if (dupes.length) throw new Error(`duplicate worker names: ${[...new Set(dupes)].join(', ')}`);
const slugs = names.map(n => n.toLowerCase().replace(/[^a-z0-9]+/g, '-'));
if (new Set(slugs).size !== slugs.length) throw new Error('workers collapse to duplicate slugs'); Try / catch
try {
const plan = buildOrchestrationPlan(config);
} catch (err) {
if (String(err.message).includes('unique slugs')) {
console.error('Fix worker names in config — duplicates found:', err.message);
process.exitCode = 1;
return;
}
throw err;
} Prevention
- Enforce unique worker names in config schema validation (zod/ajv) before planning
- Programmatically suffix generated worker names with an index
- Keep names distinct alphanumerically, not just by case or punctuation
- Add a CI lint that runs buildOrchestrationPlan against config fixtures
When it happens
Trigger: Calling plan() (which calls buildOrchestrationPlan) with a config where two workers share the same name, or where names differ only by characters that slugify strips/normalizes (e.g. 'Worker 1' and 'worker-1', or 'My Agent!' and 'my-agent').
Common situations: Copy-pasting a worker entry in the orchestration config and forgetting to rename it; renaming workers to names that collapse to the same slug after normalization; generating workers programmatically without unique names.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- asset name must be a simple filename stem (letters, digits…
- Choose at least one guided harness: Claude, Codex, or Kimi.
- Draft name must be a plain file name
- failure.reason
- genre has no references
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/a6628fa9029f9e72.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/tmux-worktree-orchestrator.js:203
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);
const seedPaths = normalizeSeedPaths([...globalSeedPaths, ...workerSeedPaths], repoRoot);
const templateVariables = buildTemplateVariables({
branch_name: branchName,
handoff_file: handoffFilePath,
repo_root: repoRoot,
session_name: sessionName,
status_file: statusFilePath,View on GitHub (pinned to 8321021c54)