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

  1. Give every worker a unique name that produces a unique slug.
  2. Compute slugs upstream and de-duplicate before calling buildOrchestrationPlan: add a numeric suffix on collision.
  3. Avoid relying on the worker-N fallback when name is omitted — give explicit names.
  4. 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

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


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