{"record":{"id":"d51be4679f19425f","repo":"PaddlePaddle/PaddleOCR","slug":"docx-conversion-requires-python-docx-pip-install","errorCode":null,"errorMessage":"DOCX conversion requires python-docx: pip install paddleocr[doc2md]","messagePattern":"DOCX conversion requires python-docx: pip install paddleocr\\[doc2md\\]","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"paddleocr/_doc2md/converters/docx.py","lineNumber":1294,"sourceCode":"    ilvl = int(ilvl_elem.get(f\"{_W}val\", \"0\")) if ilvl_elem is not None else 0\n    if num_id not in numbering_map:\n        return None\n    fmt = numbering_map[num_id].get(ilvl, \"bullet\")\n    list_type = (\n        \"ordered\"\n        if fmt in (\"decimal\", \"lowerLetter\", \"upperLetter\", \"lowerRoman\", \"upperRoman\")\n        else \"bullet\"\n    )\n    return (list_type, ilvl, num_id)\n\n\ndef _convert_body(doc, *, extract_drawings=True) -> tuple:\n    \"\"\"Traverse body elements in order and produce Markdown. Returns (markdown_str, images_dict).\"\"\"\n    try:\n        from docx.table import Table\n        from docx.text.paragraph import Paragraph\n    except ImportError:\n        raise RuntimeError(\n            \"DOCX conversion requires python-docx: pip install paddleocr[doc2md]\"\n        )\n\n    body_font_size = _get_body_font_size(doc)\n    content_width = _get_content_width(doc)\n    numbering_map = _build_numbering_map(doc)\n    lines: list[str] = []\n    images: dict = {}\n    image_counter = [0]  # wrapped in list so inner functions can mutate it\n    code_buf: list[str] = []  # buffer for consecutive code paragraphs\n    toc_buf: list[tuple] = []  # buffer for consecutive TOC paragraphs\n    ol_counters: dict[str, int] = {}  # key = \"{numId}-{ilvl}\", value = current index\n    prev_was_list = False\n\n    def flush_code_buf():\n        \"\"\"Flush the code buffer as a fenced code block.\"\"\"\n        if code_buf:\n            lines.append(\"```\")","sourceCodeStart":1276,"sourceCodeEnd":1312,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_doc2md/converters/docx.py#L1276-L1312","documentation":"RuntimeError raised inside _convert_body when the lazy 'from docx.table/text.paragraph import ...' fails. python-docx is an optional dependency of the doc2md extra; DOCX-to-Markdown conversion cannot proceed without it, and the message tells you the exact extra to install.","triggerScenarios":"Calling doc2md conversion on a .docx file (which routes to _convert_body via the DocxConverter) without python-docx installed.","commonSituations":"Installing plain 'paddleocr' without extras and later using doc2md; sparse production images that trimmed optional deps.","solutions":["pip install 'paddleocr[doc2md]'","Or install the dependency directly: pip install python-docx","Verify with: python -c \"import docx; print(docx.__version__)\""],"exampleFix":"# before (shell)\npip install paddleocr\n# after\npip install 'paddleocr[doc2md]'","handlingStrategy":"validation","validationCode":"try:\n    import docx  # noqa: F401\n    HAS_DOCX = True\nexcept ImportError:\n    HAS_DOCX = False\n\nif not HAS_DOCX:\n    raise RuntimeError(\"install paddleocr[doc2md] before converting .docx\")","typeGuard":null,"tryCatchPattern":"try:\n    doc2md_convert('file.docx')\nexcept RuntimeError as e:\n    if 'python-docx' in str(e):\n        subprocess.run([sys.executable, '-m', 'pip', 'install', 'paddleocr[doc2md]'])\n    else:\n        raise","preventionTips":["Install the doc2md extra in images that will convert documents","Probe optional imports at startup, not per file"],"tags":["optional-dependency","doc2md","docx","installation"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}