abhigyanpatwari/GitNexus · error · BadRequestError

Could not allocate an upload directory after

Error message

Could not allocate an upload directory after ${MAX_NAME_COLLISION_TRIES} attempts

What it means

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.

Solutions

  1. Delete or archive old upload directories under the upload root (inside the server's GITNEXUS_HOME data volume) and retry
  2. Rename the top-level folder before uploading so the derived base name differs
  3. Add periodic cleanup that removes upload dirs whose analysis jobs are terminal

Example fix

# before: 101 uploads of 'my-project' exist
rm -rf "$GITNEXUS_HOME"/uploads/my-project*  # or archive them; then re-upload

# after: unique name avoids the collision entirely
mv my-project my-project-run-42 && upload again
Defensive patterns

Strategy: retry

Validate before calling

// client-side: make collisions unlikely before uploading
const uniqueFolderName = `${baseFolderName}-${Date.now().toString(36)}`;
formData.set('name', uniqueFolderName);

Type guard

function willNotCollide(base, existingNames) {
  const candidates = [base, ...Array.from({ length: 100 }, (_, i) => `${base}-${i + 2}`)];
  return candidates.some((c) => !existingNames.includes(c));
}

Try / catch

// server operator: free suffix space, then the caller retries
try { await postUpload(formData); }
catch (e) {
  if (/Could not allocate an upload directory/.test(String(e.message))) {
    await cleanupTerminalUploadDirs(); // rm/archive finished upload dirs under GITNEXUS_HOME uploads
    return postUpload(formData);
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-08-20). Data as JSON: /api/errors/9d7a625723f48dd7. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/server/analyze-upload.ts:65

 * collision with an existing upload. Bounded to avoid an unbounded scan.
 */
async function pickAvailableName(base: string): Promise<string> {
  for (let i = 0; i < MAX_NAME_COLLISION_TRIES; i++) {
    const name = i === 0 ? base : `${base}-${i + 1}`;
    let dir: string;
    try {
      dir = getUploadDir(name);
    } catch {
      continue;
    }
    try {
      await fsp.access(dir);
      // exists → try the next suffix
    } catch {
      return name; // ENOENT → available
    }
  }
  throw new BadRequestError(
    `Could not allocate an upload directory after ${MAX_NAME_COLLISION_TRIES} attempts`,
    409,
  );
}

export function createAnalyzeUploadHandler(deps: AnalyzeUploadDeps) {
  const ingest = deps.ingest ?? ingestUpload;

  return async function handleAnalyzeUploadRequest(req: Request, res: Response): Promise<void> {
    let stageRoot: string | undefined;
    let promotedDir: string | undefined;
    let createdJobId: string | undefined;
    let launched = false;
    try {
      const result = await ingest(req as IncomingMessage);
      stageRoot = result.stageRoot;

      const baseName = deriveUploadName(result.topLevelName);

View on GitHub (pinned to ac9a4e9abd)