affaan-m/ECC · error
Workers must have unique slugs — duplicate: ${workerSlug}
Error message
Workers must have unique slugs — duplicate: ${workerSlug} What it means
Thrown inside the workers.map loop when slugify(worker.name) (or the fallback worker-N) produces a slug that has already been seen in this plan. Slugs become branch names (orchestrator-<session>-<slug>), worktree paths, and coordination directory names — duplicates would collide on the filesystem and in git. The check uses a Set of seen slugs, so the second occurrence triggers the error.
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 01e15490f0)
Solutions
- Give every worker a unique name that produces a unique slug.
- Compute slugs upstream and de-duplicate before calling buildOrchestrationPlan: add a numeric suffix on collision.
- Avoid relying on the worker-N fallback when name is omitted — give explicit names.
- Remember slugify lowercases, replaces non-[a-z0-9] runs with '-', and trims leading/trailing dashes; design names accordingly.
Example fix
// before
workers: [
{ name: 'Auth Fix', task: '...' },
{ name: 'auth-fix', task: '...' }, // same slug
]
// -> Workers must have unique slugs — duplicate: auth-fix
// after
workers: [
{ name: 'auth-fix-tests', task: '...' },
{ name: 'auth-fix-impl', task: '...' },
] Defensive patterns
Strategy: validation
Validate before calling
function slugify(value, fallback = 'worker') {
const n = String(value || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
return n || fallback;
}
function dedupeWorkerNames(workers) {
const seen = new Set();
return workers.map((w, i) => {
let slug = slugify(w.name || `worker-${i + 1}`);
while (seen.has(slug)) slug += `-${i + 1}`;
seen.add(slug);
return { ...w, name: w.name || slug };
});
}
config.workers = dedupeWorkerNames(config.workers); Type guard
function hasUniqueSlugs(workers) {
const slugs = workers.map((w, i) => slugify(w.name || `worker-${i + 1}`));
return new Set(slugs).size === slugs.length;
} Try / catch
try {
buildOrchestrationPlan(config);
} catch (error) {
if (/Workers must have unique slugs/.test(error.message)) {
config.workers = dedupeWorkerNames(config.workers);
buildOrchestrationPlan(config);
return;
}
throw error;
} Prevention
- Slugify names yourself and de-duplicate by appending a numeric suffix on collision.
- Remember slugify is case-insensitive and strips punctuation — 'Auth Fix' and 'auth-fix' collide.
- Give every worker an explicit, distinctive name rather than relying on the worker-N fallback.
- Add a uniqueness unit test for any worker-list builder.
When it happens
Trigger: Two workers named 'Worker' both slugify to 'worker'; workers named 'auth-fix' and 'auth fix' (the space becomes a hyphen, colliding with the explicitly-named 'auth-fix'); workers with no name that both fall back to 'worker-1' (this can happen if name is omitted on multiple entries — though normally the index differs, explicit duplicate names are the usual cause); names that differ only in punctuation/case.
Common situations: A config generator stamps every worker with a generic name; copy-paste of a worker block without renaming; case-insensitive thinking ('Alpha' and 'alpha' both slugify to 'alpha'); names that differ only in characters slugify strips (e.g. 'auth.fix' and 'auth fix' and 'auth-fix').
Related errors
- ${label} must be a lowercase letters/numbers slug.
- launcherCommand must be a non-empty string
- Unknown template variable: ${key}
- seedPaths entries must stay inside repoRoot: ${entry}
- Seed path does not exist in repoRoot: ${seedPath}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/a6628fa9029f9e72.
Report an issue: GitHub.