{"record":{"id":"36f506b0fb6f96ca","repo":"abhigyanpatwari/GitNexus","slug":"uploaded-folder-has-no-usable-name","errorCode":null,"errorMessage":"Uploaded folder has no usable name","messagePattern":"Uploaded folder has no usable name","errorType":"http","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"gitnexus/src/server/analyze-upload.ts","lineNumber":81,"sourceCode":"    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);\n      if (!baseName) {\n        throw new BadRequestError('Uploaded folder has no usable name');\n      }\n\n      // webkitRelativePath prefixes every entry with the picked folder, so the\n      // real repo root is stageRoot/<topLevelName>. Validate it is a directory\n      // BEFORE taking the single analysis slot — a malformed (non-folder)\n      // upload must not be able to occupy the slot.\n      const innerRoot = path.join(result.stageRoot, result.topLevelName);\n      let innerIsDir = false;\n      try {\n        innerIsDir = (await fsp.stat(innerRoot)).isDirectory();\n      } catch {\n        innerIsDir = false;\n      }\n      if (!innerIsDir) {\n        throw new BadRequestError('Upload must be a folder');\n      }\n\n      const finalName = await pickAvailableName(baseName);","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/analyze-upload.ts#L63-L99","documentation":"After multipart ingestion, the handler derives a filesystem-safe name from the upload's top-level folder via deriveUploadName, which sanitizes and returns null when the result is 'unknown', '.', '..', or starts with '.'. A null result becomes BadRequestError 400 'Uploaded folder has no usable name' — by design, so un-nameable folders are rejected instead of everyone colliding on UPLOAD_ROOT/unknown.","triggerScenarios":"POST /api/analyze/upload where the picked folder's name contains no characters from the safe set [A-Za-z0-9._-] after sanitization (e.g. '===', '***', emoji-only, whitespace-only), or is a dot-folder like '.config'.","commonSituations":"Uploading hidden/dot-prefixed directories from OS tools; folders named entirely in scripts/unicode that sanitizeRepoName strips to the 'unknown' sentinel; unusual browser-picker folder names.","solutions":["Rename the folder to alphanumerics plus . _ - before uploading","Avoid a leading dot in the folder name","If the name contains spaces/unicode, transliterate or simplify it first"],"exampleFix":"# before\nfolder name: '.hidden-config' -> 400 Uploaded folder has no usable name\n\n# after\nfolder name: 'hidden-config' -> accepted","handlingStrategy":"validation","validationCode":"const SAFE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;\nfunction hasUsableFolderName(name) { return SAFE.test(name); }\n// run before building the FormData\nif (!hasUsableFolderName(topLevelName)) alertUser('Rename the folder to letters/numbers/._- without a leading dot');","typeGuard":"function isUploadableFolderName(name) {\n  return typeof name === 'string' && name.length > 0 && !name.startsWith('.') && /^[a-zA-Z0-9._-]+$/.test(name) && name !== 'unknown';\n}","tryCatchPattern":"try { await fetch('/api/analyze/upload', { method: 'POST', body: formData }); }\ncatch (e) {\n  if (e.status === 400 && /no usable name/.test(e.message)) promptUserToRenameFolder();\n  else throw e;\n}","preventionTips":["Validate the picked folder name client-side before upload","Warn on dot-prefix and punctuation/emoji-only folder names","Default auto-generated names to a sanitized base plus unique suffix"],"tags":["upload","filename-sanitization","http-400","validation"],"backgroundTag":"invalid-filename","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}