{"record":{"id":"9d7a625723f48dd7","repo":"abhigyanpatwari/GitNexus","slug":"could-not-allocate-an-upload-directory-after-max","errorCode":null,"errorMessage":"Could not allocate an upload directory after ${MAX_NAME_COLLISION_TRIES} attempts","messagePattern":"Could not allocate an upload directory after (.+?) attempts","errorType":"http","errorClass":"BadRequestError","httpStatus":409,"severity":"error","filePath":"gitnexus/src/server/analyze-upload.ts","lineNumber":61,"sourceCode":" * collision with an existing upload. Bounded to avoid an unbounded scan.\n */\nasync function pickAvailableName(base: string): Promise<string> {\n  for (let i = 0; i < MAX_NAME_COLLISION_TRIES; i++) {\n    const name = i === 0 ? base : `${base}-${i + 1}`;\n    let dir: string;\n    try {\n      dir = getUploadDir(name);\n    } catch {\n      continue;\n    }\n    try {\n      await fsp.access(dir);\n      // exists → try the next suffix\n    } catch {\n      return name; // ENOENT → available\n    }\n  }\n  throw new BadRequestError(\n    `Could not allocate an upload directory after ${MAX_NAME_COLLISION_TRIES} attempts`,\n    409,\n  );\n}\n\nexport function createAnalyzeUploadHandler(deps: AnalyzeUploadDeps) {\n  const ingest = deps.ingest ?? ingestUpload;\n\n  return async function handleAnalyzeUploadRequest(req: Request, res: Response): Promise<void> {\n    let stageRoot: string | undefined;\n    let promotedDir: string | undefined;\n    let createdJobId: string | undefined;\n    let launched = false;\n    try {\n      const result = await ingest(req as IncomingMessage);\n      stageRoot = result.stageRoot;\n\n      const baseName = deriveUploadName(result.topLevelName);","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/analyze-upload.ts#L43-L79","documentation":"pickAvailableName in analyze-upload.ts tries the sanitized upload name and then -2, -3, ... suffixes under UPLOAD_ROOT, up to MAX_NAME_COLLISION_TRIES=100 attempts; if every candidate directory already exists (or getUploadDir rejects the name), it throws BadRequestError 409. It exists to bound the collision scan instead of looping forever.","triggerScenarios":"POST /api/analyze/upload of a folder whose derived name already has 100 suffixed siblings (base, base-2 ... base-101 all present); typically after ~100 uploads of the same-named folder with no cleanup of old upload directories.","commonSituations":"CI pipelines or classroom/demo setups repeatedly uploading the identically-named folder; an operator never pruning UPLOAD_ROOT under the server's data dir (GITNEXUS_HOME); long-lived self-hosted instances.","solutions":["Delete or archive old upload directories under the upload root (inside the server's GITNEXUS_HOME data volume) and retry","Rename the top-level folder before uploading so the derived base name differs","Add periodic cleanup that removes upload dirs whose analysis jobs are terminal"],"exampleFix":"# before: 101 uploads of 'my-project' exist\nrm -rf \"$GITNEXUS_HOME\"/uploads/my-project*  # or archive them; then re-upload\n\n# after: unique name avoids the collision entirely\nmv my-project my-project-run-42 && upload again","handlingStrategy":"retry","validationCode":"// client-side: make collisions unlikely before uploading\nconst uniqueFolderName = `${baseFolderName}-${Date.now().toString(36)}`;\nformData.set('name', uniqueFolderName);","typeGuard":"function willNotCollide(base, existingNames) {\n  const candidates = [base, ...Array.from({ length: 100 }, (_, i) => `${base}-${i + 2}`)];\n  return candidates.some((c) => !existingNames.includes(c));\n}","tryCatchPattern":"// server operator: free suffix space, then the caller retries\ntry { await postUpload(formData); }\ncatch (e) {\n  if (/Could not allocate an upload directory/.test(String(e.message))) {\n    await cleanupTerminalUploadDirs(); // rm/archive finished upload dirs under GITNEXUS_HOME uploads\n    return postUpload(formData);\n  }\n  throw e;\n}","preventionTips":["Suffix the uploaded folder name with a run id (timestamp/CI build number)","Schedule cleanup of upload directories whose jobs are terminal","Alert when the count of same-base upload dirs approaches 100"],"tags":["upload","name-collision","http-409","resource-exhaustion"],"backgroundTag":"directory-name-collision","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}