abhigyanpatwari/GitNexus · error · BadRequestError
Too many directories in upload
Error message
Too many directories in upload
What it means
Thrown by the multipart folder-upload ingest pipeline when the number of directories it materializes inside the staging sandbox exceeds UploadLimits.maxDirs (default 50,000). Every uploaded file's parent path is created via mkdirContained (mkdir -p semantics), and each newly created directory increments a counter; crossing the limit aborts the upload with HTTP 413. It is the directory-count sibling of the parallel maxTotalBytes (250 MB default) byte cap, guarding against inode/disk exhaustion from a directory 'zip bomb'.
Source
Thrown at gitnexus/src/server/upload-ingest.ts:137
const segs = relParent.split(path.sep).filter(Boolean);
let cur = stageRoot;
for (const seg of segs) {
cur = path.join(cur, seg);
let made = false;
try {
fs.mkdirSync(cur);
made = true;
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err;
}
const st = fs.lstatSync(cur);
if (st.isSymbolicLink() || !st.isDirectory()) {
throw new BadRequestError('Upload path escapes the sandbox');
}
if (made) {
state.dirCount++;
if (state.dirCount > state.limits.maxDirs) {
throw new BadRequestError('Too many directories in upload', 413);
}
}
}
}
export interface IngestOptions {
/** Override the staging parent dir (defaults to UPLOAD_ROOT; for tests). */
root?: string;
}
/**
* Parse and securely write a multipart folder upload into a fresh staging
* directory under UPLOAD_ROOT. Resolves with the populated staging dir, or
* rejects with a BadRequestError (status 400/413) after removing the staging
* dir. The caller owns promotion + cleanup of the returned `stageRoot`.
*/
export async function ingestUpload(
req: IncomingMessage,View on GitHub (pinned to aac7515d2a)
Solutions
- Exclude heavy generated trees (node_modules, build output, .venv) from the upload so distinct directory count stays under 50,000
- If the upload is legitimately that large, raise limits.maxDirs (and maxTotalBytes) in the UploadLimits object passed to the ingest handler
- Pre-validate client-side: count distinct parent directories of the selected files before POSTing and split or trim the upload
- If you operate the server, put the upload endpoint behind auth/rate limits so strangers cannot force 413 churn
Example fix
// before
const limits = { ...DEFAULT_UPLOAD_LIMITS }; // maxDirs: 50000
// after — allow very large trees knowingly
const limits = {
...DEFAULT_UPLOAD_LIMITS,
maxDirs: 200_000,
maxTotalBytes: 1024 * 1024 * 1024,
}; Defensive patterns
Strategy: validation
Validate before calling
function countDistinctDirs(files: { relativePath: string }[]): number {
const dirs = new Set<string>();
for (const f of files) {
const parts = f.relativePath.split('/').filter(Boolean);
for (let i = 1; i < parts.length; i++) dirs.add(parts.slice(0, i).join('/'));
}
return dirs.size;
}
if (countDistinctDirs(files) > 50_000) {
throw new Error('Too many directories — trim the folder or split the upload');
} Prevention
- Count distinct parent directories client-side before POSTing and compare against the 50,000 default
- Strip node_modules and build artifacts from folder uploads
- Treat repeated 413s from this endpoint as a signal to split the upload, not to retry it unchanged
When it happens
Trigger: POSTing a multipart folder upload to the ingest endpoint where the cumulative count of distinct newly created parent directories exceeds maxDirs — e.g. a tree with more than 50,000 distinct folders, or many files whose relative paths each introduce unique deep parent chains.
Common situations: Uploading node_modules or a vendored toolchain along with the project; uploading a repo mirror/backup full of generated directories; a deliberately crafted multipart payload trying to exhaust the staging filesystem; tests that pass a small custom limits object and forget to scale it.
Related errors
- Could not allocate an upload directory after ${MAX_NAME_COLL
- Upload must be a folder
- Invalid upload path
- Upload path too long
- Upload must be a single folder of files
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/22cb98d6e8301c82.
Report an issue: GitHub.