{"record":{"id":"90243ad47af24099","repo":"HKUDS/DeepTutor","slug":"duplicate-filename-after-sanitization-duplicate","errorCode":null,"errorMessage":"Duplicate filename after sanitization: '{duplicate_key}'. Rename one of the files and try again.","messagePattern":"Duplicate filename after sanitization: '(.+?)'\\. Rename one of the files and try again\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"deeptutor/api/routers/knowledge.py","lineNumber":556,"sourceCode":"                size_bytes,\n                allowed_extensions=allowed_extensions,\n            )\n        except Exception as e:\n            error_message = (\n                f\"Validation failed for file '{original_filename}': {format_exception_message(e)}\"\n            )\n            raise HTTPException(status_code=400, detail=error_message) from e\n\n        rel = (\n            rel_paths[idx].replace(\"\\\\\", \"/\")\n            if rel_paths and idx < len(rel_paths) and rel_paths[idx]\n            else \"\"\n        )\n        subdir = _sanitize_rel_subdir(rel.rsplit(\"/\", 1)[0]) if \"/\" in rel else \"\"\n        duplicate_key = f\"{subdir}/{sanitized_filename}\" if subdir else sanitized_filename\n\n        if duplicate_key in seen_names:\n            raise HTTPException(\n                status_code=400,\n                detail=(\n                    f\"Duplicate filename after sanitization: '{duplicate_key}'. \"\n                    \"Rename one of the files and try again.\"\n                ),\n            )\n\n        seen_names.add(duplicate_key)\n        validated.append(\n            {\n                \"original_filename\": original_filename,\n                \"sanitized_filename\": sanitized_filename,\n                \"path\": duplicate_key,\n                \"size_bytes\": size_bytes,\n            }\n        )\n\n    return validated","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/api/routers/knowledge.py#L538-L574","documentation":"Raised in _validate_upload_batch when two files in one request sanitize to the same subdir/filename key (e.g. 'report.PDF' and 'report.pdf', or 'a b.txt' and 'a_b.txt' after segment sanitization). Sanitization is lossy, so distinct raw names can collide; the batch is rejected to avoid silent overwrites.","triggerScenarios":"Uploading multiple files whose names differ only by case, whitespace, or characters stripped by the sanitizer; or two files explicitly targeting the same rel_path in a batch upload.","commonSituations":"Windows/macOS case-insensitive origins exporting both 'Notes.md' and 'notes.md'; bulk uploads from different sources with near-identical names.","solutions":["Rename one of the colliding files before uploading","Place the files in different subfolders via rel_paths so the keys differ","Deduplicate the batch locally (case-insensitive name check) before sending"],"exampleFix":"# before\nfiles: ['Report.pdf', 'report.pdf']\n# after\nfiles: ['Report-v1.pdf', 'Report-v2.pdf']","handlingStrategy":"validation","validationCode":"import re\n\ndef dedupe_keys(files, rel_paths=None):\n    seen = set()\n    for i, name in enumerate(files):\n        sub = (rel_paths[i] if rel_paths and rel_paths[i] else '').rsplit('/',1)[0]\n        sub = re.sub(r'[^\\w-]','_', sub.strip()).lower()\n        key = f\"{sub}/{sanitize(name)}\" if sub else sanitize(name)\n        if key in seen: raise ValueError(f'collision: {key}')\n        seen.add(key)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Case-insensitive uniqueness-check filenames in a batch","Use distinct subfolders for same-named files","Replicate the sanitizer (strip/rename) client-side before upload"],"tags":["upload","filename-collision","sanitization","http-400"],"backgroundTag":"filename-collision","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}