affaan-m/ECC · error

Too many task directories.

Error message

Too many task directories.

What it means

collectTaskFiles scans a coordination directory for per-task subdirectories and throws 'Too many task directories.' when more than 64 non-hidden subdirectories are found. This is a fan-out guard: the inventory refuses to iterate an unbounded number of task folders, treating an excessive count as suspicious or a misconfiguration rather than silently scanning everything.

Solutions

  1. Clean up stale task directories: archive or delete folders for completed/abandoned tasks until the count is at or below 64.
  2. Verify the directory argument is the intended coordination/tasks root, not a broader parent directory; fix the path or config if wrong.
  3. If more than 64 concurrent tasks is legitimate for your workflow, raise the 64-entry cap in collectTaskFiles (scripts/lib/coordination-inventory.js:241) or shard tasks across multiple directories.
  4. Because collectTaskFiles swallows the error and returns status 'unavailable', check the inventory output field to confirm this guard — not a permissions problem — is the actual cause.

Example fix

// before: guard trips after unbounded task accumulation
if (entries.length > 64) throw new Error('Too many task directories.');

// after: archive completed tasks before scanning, keep cap meaningful
archiveCompletedTasks(directory, 64);
const entries = fs.readdirSync(directory, { withFileTypes: true }).filter(e => e.isDirectory() && !e.name.startsWith('.'));
if (entries.length > 64) throw new Error('Too many task directories.');
Defensive patterns

Strategy: validation

Validate before calling

const count = fs.readdirSync(tasksDir, { withFileTypes: true })
  .filter(e => e.isDirectory() && !e.name.startsWith('.')).length;
if (count > 64) throw new Error(`${tasksDir} has ${count} task dirs; archive completed tasks first`);

Try / catch

const result = collectTaskFiles(dir);
if (result.status === 'unavailable') {
  // likely the >64-directories guard (or an IO error) — inspect and prune dir, then retry
  pruneStaleTaskDirs(dir);
}

Prevention

When it happens

Trigger: Calling collectTaskFiles(directory) (directly or via main) when the directory contains 65 or more visible subdirectories (entries that are directories and do not start with '.'). Note the error is caught internally and the function returns { status: 'unavailable' }.

Common situations: Many agents/CI jobs each created a task directory that was never cleaned up after completion, the script was pointed at the wrong root (e.g. a workspaces root containing dozens of projects instead of the coordination dir), or tests/fixtures accumulated hundreds of task folders over time.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/164d19dae73eb7d6. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/coordination-inventory.js:241

  const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
  try {
    const stat = fs.fstatSync(fd);
    if (!stat.isFile() || stat.size > limit) throw new Error('Input exceeds file limit.');
    const buffer = Buffer.alloc(limit + 1);
    let size = 0; let count;
    do { count = fs.readSync(fd, buffer, size, buffer.length - size, null); size += count; } while (count && size < buffer.length);
    if (size > limit) throw new Error('Input exceeds file limit.');
    return { content: buffer.subarray(0,size).toString('utf8'), modifiedAt: stat.mtime.toISOString() };
  } finally { fs.closeSync(fd); }
}
function readJson(file) {
  try { return JSON.parse(readBounded(file).content); }
  catch (error) { throw new Error(error.message === 'Input exceeds file limit.' ? error.message : 'Cannot read coordination JSON.'); }
}
function collectTaskFiles(directory) {
  try {
    const entries = fs.readdirSync(directory, { withFileTypes: true }).filter(e => e.isDirectory() && !e.name.startsWith('.')).sort((a,b) => a.name < b.name ? -1 : 1);
    if (entries.length > 64) throw new Error('Too many task directories.');
    const tasks = []; const unreadable = [];
    for (const entry of entries) {
      let loaded = false;
      for (const name of ['STATUS.md', 'status.md']) {
        try {
          const data = readBounded(path.join(directory, entry.name, name), 65536);
          const parsed = parseWorkerStatus(data.content);
          let heartbeatAt = null;
          try { if (parsed.updated) heartbeatAt = timestamp(parsed.updated); } catch { /* Unknown timestamp, not a heartbeat. */ }
          tasks.push(normalizeTask({ id: entry.name, paths: [], status: parsed.state || 'unknown', heartbeatAt, statusFileModifiedAt: data.modifiedAt }));
          loaded = true; break;
        } catch { /* Try legacy lowercase status filename; report unreadable below. */ }
      }
      if (!loaded) unreadable.push(entry.name);
    }
    return { status: unreadable.length ? 'partial' : 'ok', tasks, unreadable };
  } catch { return { status: 'unavailable', tasks: [], unreadable: [] }; }
}

View on GitHub (pinned to 8321021c54)