{"record":{"id":"4ad901da65ad3272","repo":"opendatalab/MinerU","slug":"unsupported-file-type-file-suffix","errorCode":null,"errorMessage":"Unsupported file type: {file_suffix}","messagePattern":"Unsupported file type: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"mineru/cli/fast_api.py","lineNumber":767,"sourceCode":"    uploads: list[StoredUpload] = []\n\n    for upload in files:\n        original_name = upload.filename or f\"upload-{uuid.uuid4()}\"\n        filename = normalize_upload_filename(original_name)\n        normalized_stem = normalize_task_stem(Path(filename).stem)\n        destination = build_upload_destination(upload_dir, filename)\n        try:\n            with open(destination, \"wb\") as handle:\n                while True:\n                    chunk = await upload.read(1 << 20)\n                    if not chunk:\n                        break\n                    handle.write(chunk)\n\n            file_suffix = guess_suffix_by_path(destination)\n            if file_suffix not in SUPPORTED_UPLOAD_SUFFIXES:\n                cleanup_file(str(destination))\n                raise HTTPException(\n                    status_code=400,\n                    detail=f\"Unsupported file type: {file_suffix}\",\n                )\n\n            uploads.append(\n                StoredUpload(\n                    original_name=original_name,\n                    stem=normalized_stem,\n                    path=str(destination),\n                )\n            )\n        except Exception:\n            cleanup_file(str(destination))\n            raise\n        finally:\n            await upload.close()\n\n    normalized_stems, renamed_stems = uniquify_task_stems(","sourceCodeStart":749,"sourceCodeEnd":785,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/cli/fast_api.py#L749-L785","documentation":"HTTP 400 raised by the fast_api upload endpoint after the uploaded bytes were written to disk: the file's suffix (from filename and/or content sniffing via guess_suffix_by_path) is not in SUPPORTED_UPLOAD_SUFFIXES, which is pdf_suffixes + image_suffixes + office_suffixes = pdf, png, jpeg, jp2, webp, gif, bmp, jpg, tiff, docx, pptx, xlsx. The partially stored file is deleted before the exception is raised. Note guess_suffix_by_path can return 'unknown' when the content does not match any known magic bytes, so even a named .pdf can be rejected if it is not actually a PDF.","triggerScenarios":"POST /file_parse or the async parse endpoint with a .doc/.ppt/.xls, .txt, .md, .html, or any other unsupported extension; uploading a file whose extension is fine but whose bytes are not recognizable (renamed file, corrupted upload, truncated body) so suffix detection returns 'unknown'; double extension like file.pdf.exe.","commonSituations":"Assuming legacy Office formats are accepted because docx/pptx/xlsx are; content sniffing disagreeing with the filename after a bad proxy/encoding step; tests posting dummy bytes with a .pdf name.","solutions":["Re-check the uploaded file type: only pdf, png, jpeg/jpg, jp2, webp, gif, bmp, tiff, docx, pptx, xlsx are accepted.","Convert legacy Office files (.doc/.ppt/.xls) to their x-variants before upload.","If the extension looks right, verify the file opens locally and is not corrupted/truncated; compare magic bytes (e.g. %PDF- for pdf).","Client-side, read the 400 detail in the response body — it names the detected suffix, which tells you whether it is an extension problem or 'unknown' content."],"exampleFix":"# before\nfiles = {'files': open('report.doc', 'rb')}\nrequests.post('http://127.0.0.1:8000/file_parse', files=files)  # 400 Unsupported file type: doc\n\n# after\n# convert first: soffice --headless --convert-to docx report.doc\nfiles = {'files': open('report.docx', 'rb')}\nrequests.post('http://127.0.0.1:8000/file_parse', files=files)","handlingStrategy":"validation","validationCode":"ALLOWED = {'pdf', 'png', 'jpeg', 'jpg', 'jp2', 'webp', 'gif', 'bmp', 'tiff', 'docx', 'pptx', 'xlsx'}\n\ndef can_upload(name: str) -> bool:\n    return name.rsplit('.', 1)[-1].lower() in ALLOWED if '.' in name else False","typeGuard":"def is_supported_upload(filename: str) -> bool:\n    return can_upload(filename)","tryCatchPattern":"# requests\nr = requests.post(url, files=files)\nif r.status_code == 400 and 'Unsupported file type' in r.text:\n    detected = r.json().get('detail', '').rsplit(':', 1)[-1].strip()\n    if detected == 'unknown':\n        raise RuntimeError('File content unrecognizable — corrupted upload?')\n    raise ValueError(f'Convert {detected} files before uploading')","preventionTips":["Filter file pickers and drag-drop UIs to the supported extension list before upload.","For 'unknown' detections, verify magic bytes client-side (%PDF-, PK zip header for office).","Never rely on the filename alone — the server sniffs content too."],"tags":["mineru","fastapi","http-400","upload","file-format"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}