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
- Clean up stale task directories: archive or delete folders for completed/abandoned tasks until the count is at or below 64.
- Verify the directory argument is the intended coordination/tasks root, not a broader parent directory; fix the path or config if wrong.
- 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.
- 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
- Archive or delete task directories as part of task completion, not later.
- Run a periodic cleanup job so the directory stays under the 64-dir cap.
- Point the tool at the specific tasks directory, not a broad parent.
- Check result.status === 'unavailable' in output — collectTaskFiles swallows this error.
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
- Cannot read coordination JSON.
- all overlays must be readable local files
- all takes must be readable local files
- artifact byte count exceeded during reading
- artifact byte count mismatch
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)