[{"data":1,"prerenderedAt":1977},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported":1941,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported":1950,"children:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported":1955,"related:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported":1956},{"id":4,"title":5,"body":6,"dateModified":1907,"datePublished":1907,"description":1908,"extension":1909,"faq":1910,"keyword":1919,"kind":1920,"meta":1921,"navigation":330,"path":1929,"seo":1930,"slug":1933,"stem":1934,"topics":1935,"type":1939,"__hash__":1940},"docs\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported\u002Findex.md","Fix \"XLRDError: Excel xlsx file; not supported\" in Python",{"type":7,"value":8,"toc":1892},"minimark",[9,18,28,56,230,235,253,256,260,300,303,406,410,413,510,520,524,536,567,573,758,772,776,790,953,964,1133,1137,1140,1146,1156,1270,1273,1439,1458,1462,1468,1504,1517,1521,1527,1705,1721,1725,1780,1784,1799,1803,1827,1831,1837,1843,1849,1855,1859,1888],[10,11,12,13,17],"p",{},"A script that used to work now stops on the first ",[14,15,16],"code",{},"read_excel"," call:",[19,20,26],"pre",{"className":21,"code":23,"language":24,"meta":25},[22],"language-text","xlrd.biffh.XLRDError: Excel xlsx file; not supported\n","text","",[14,27,23],{"__ignoreMap":25},[10,29,30,31,34,35,38,39,42,43,46,47,49,50,55],{},"The message is accurate. xlrd 2.0, released in December 2020, dropped support for everything except the old binary ",[14,32,33],{},".xls"," format. Any code that hands an ",[14,36,37],{},".xlsx"," workbook to xlrd — directly through ",[14,40,41],{},"xlrd.open_workbook",", or indirectly through pandas with ",[14,44,45],{},"engine=\"xlrd\""," — now gets this error. The library you need for ",[14,48,37],{}," is openpyxl, which pandas already uses by default. The work is finding which line still routes the file to xlrd. This page belongs to ",[51,52,54],"a",{"href":53},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002F","Troubleshooting Common Python Excel Errors",".",[57,58,66,67,66,71,66,75,66,83,66,89,66,99,66,110,66,116,66,121,66,129,66,136,66,142,66,147,66,150,66,155,66,160,66,167,66,172,66,176,66,179,66,182,66,187,66,193,66,198,66,202,66,205,66,208,66,211,66,216,66,220,66,224,66,227],"svg",{"viewBox":59,"role":60,"ariaLabelledBy":61,"xmlns":64,"style":65},"4 0 752 468","img",[62,63],"xlrd-routes-t","xlrd-routes-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[68,69,70],"title",{"id":62},"Which reader handles which Excel format",[72,73,74],"desc",{"id":63},"A read_excel call fans out to four readers by file format. Modern .xlsx and .xlsm go to openpyxl, .xls goes to xlrd, .xlsb goes to pyxlsb or calamine, and .ods goes to odfpy. Sending an .xlsx file down the xlrd route is what raises XLRDError.",[76,77],"rect",{"x":78,"y":79,"width":80,"height":81,"fill":82},"4","0","752","468","#ffffff",[24,84,88],{"x":85,"y":86,"style":87},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What pandas picks when you leave engine alone",[76,90],{"x":91,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":97,"style":98},"20.0","203.5","220.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[100,101,103],"g",{"transform":102},"translate(118.0,216.5) scale(1.000)",[104,105],"path",{"d":106,"fill":107,"stroke":108,"style":109},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[24,111,115],{"x":112,"y":113,"style":114},"130.0","263.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","pd.read_excel(path)",[24,117,120],{"x":112,"y":118,"style":119},"282.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","inspects the file's bytes",[76,122],{"x":123,"y":124,"width":125,"height":126,"rx":95,"fill":127,"stroke":128,"style":98},"480.0","48.0","260.0","92.0","#d9f4f1","var(--teal,#0f9488)",[100,130,132],{"transform":131},"translate(598.0,61.0) scale(1.000)",[104,133],{"d":134,"fill":107,"stroke":135,"style":109},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--teal-ink,#0b6157)",[24,137,141],{"x":138,"y":139,"style":140},"610.0","107.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","openpyxl",[24,143,146],{"x":138,"y":144,"style":145},"126.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",".xlsx and .xlsm",[104,148],{"d":149,"fill":107,"stroke":97,"style":98},"M244.0,250.0 C359.0,250.0 359.0,94.0 470.0,94.0",[151,152],"polygon",{"points":153,"fill":154},"474.0,94.0 465.0,99.0 465.0,89.0","#5b5cf0",[76,156],{"x":123,"y":157,"width":125,"height":126,"rx":95,"fill":158,"stroke":159,"style":98},"152.0","#fdefd8","var(--gold,#b4740a)",[100,161,163],{"transform":162},"translate(598.0,165.0) scale(1.000)",[104,164],{"d":165,"fill":107,"stroke":166,"style":109},"M6 3h9l4 4v14H6z M15 3v4h4","var(--gold-ink,#7a4e06)",[24,168,171],{"x":138,"y":169,"style":170},"211.0","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","xlrd",[24,173,175],{"x":138,"y":174,"style":145},"230.0",".xls only (Excel 97–2003)",[104,177],{"d":178,"fill":107,"stroke":97,"style":98},"M244.0,250.0 C359.0,250.0 359.0,198.0 470.0,198.0",[151,180],{"points":181,"fill":154},"474.0,198.0 465.0,203.0 465.0,193.0",[76,183],{"x":123,"y":184,"width":125,"height":126,"rx":95,"fill":185,"stroke":186,"style":98},"256.0","#e7ebef","var(--line,#cdd5e6)",[100,188,190],{"transform":189},"translate(598.0,269.0) scale(1.000)",[104,191],{"d":165,"fill":107,"stroke":192,"style":109},"var(--text,#172033)",[24,194,197],{"x":138,"y":195,"style":196},"315.0","font-size:13px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","pyxlsb or calamine",[24,199,201],{"x":138,"y":200,"style":145},"334.0",".xlsb",[104,203],{"d":204,"fill":107,"stroke":97,"style":98},"M244.0,250.0 C359.0,250.0 359.0,302.0 470.0,302.0",[151,206],{"points":207,"fill":154},"474.0,302.0 465.0,307.0 465.0,297.0",[76,209],{"x":123,"y":210,"width":125,"height":126,"rx":95,"fill":185,"stroke":186,"style":98},"360.0",[100,212,214],{"transform":213},"translate(598.0,373.0) scale(1.000)",[104,215],{"d":165,"fill":107,"stroke":192,"style":109},[24,217,219],{"x":138,"y":218,"style":196},"419.0","odfpy",[24,221,223],{"x":138,"y":222,"style":145},"438.0",".ods",[104,225],{"d":226,"fill":107,"stroke":97,"style":98},"M244.0,250.0 C359.0,250.0 359.0,406.0 470.0,406.0",[151,228],{"points":229,"fill":154},"474.0,406.0 465.0,411.0 465.0,401.0",[231,232,234],"h2",{"id":233},"why-xlrd-stopped-reading-xlsx","Why xlrd stopped reading .xlsx",[10,236,237,238,240,241,243,244,246,247,249,250,252],{},"xlrd was written for the binary ",[14,239,33],{}," format that Excel used until 2007. Support for ",[14,242,37],{}," was added later and was always the weaker half of the library, and the pandas 1.2 release notes cite potential security vulnerabilities in reading ",[14,245,37],{}," with xlrd 1.2 and earlier. Rather than maintain a second ",[14,248,37],{}," reader when openpyxl already did the job, the maintainers removed it and returned xlrd to its original purpose. pandas followed: from version 1.2 its default engine for ",[14,251,37],{}," became openpyxl. pandas still passes the file to xlrd if you ask for that engine explicitly — which is exactly how the error in this page's title gets raised.",[10,254,255],{},"So the error is not a bug to wait out. It is the library telling you, correctly, that it no longer reads this format — and the environment your script was written in had an older xlrd that still did. That is also why the failure tends to appear \"out of nowhere\": a server rebuild, a fresh virtual environment or a new colleague's laptop installs xlrd 2.x, and code that never changed stops working.",[231,257,259],{"id":258},"prerequisites","Prerequisites",[19,261,265],{"className":262,"code":263,"language":264,"meta":25,"style":25},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\npip install xlrd   # only if you also read legacy .xls files\n","bash",[14,266,267,286],{"__ignoreMap":25},[268,269,272,276,280,283],"span",{"class":270,"line":271},"line",1,[268,273,275],{"class":274},"sMTad","pip",[268,277,279],{"class":278},"srMev"," install",[268,281,282],{"class":278}," pandas",[268,284,285],{"class":278}," openpyxl\n",[268,287,289,291,293,296],{"class":270,"line":288},2,[268,290,275],{"class":274},[268,292,279],{"class":278},[268,294,295],{"class":278}," xlrd",[268,297,299],{"class":298},"s-wDw","   # only if you also read legacy .xls files\n",[10,301,302],{},"Check what is installed before changing anything; the fix depends on it:",[19,304,308],{"className":305,"code":306,"language":307,"meta":25,"style":25},"language-python shiki shiki-themes github-light github-dark-high-contrast","import importlib.metadata as md\n\nfor pkg in (\"pandas\", \"openpyxl\", \"xlrd\"):\n    try:\n        print(pkg, md.version(pkg))\n    except md.PackageNotFoundError:\n        print(pkg, \"not installed\")\n","python",[14,309,310,326,332,364,373,383,392],{"__ignoreMap":25},[268,311,312,316,320,323],{"class":270,"line":271},[268,313,315],{"class":314},"s-kum","import",[268,317,319],{"class":318},"skGVy"," importlib.metadata ",[268,321,322],{"class":314},"as",[268,324,325],{"class":318}," md\n",[268,327,328],{"class":270,"line":288},[268,329,331],{"emptyLinePlaceholder":330},true,"\n",[268,333,335,338,341,344,347,350,353,356,358,361],{"class":270,"line":334},3,[268,336,337],{"class":314},"for",[268,339,340],{"class":318}," pkg ",[268,342,343],{"class":314},"in",[268,345,346],{"class":318}," (",[268,348,349],{"class":278},"\"pandas\"",[268,351,352],{"class":318},", ",[268,354,355],{"class":278},"\"openpyxl\"",[268,357,352],{"class":318},[268,359,360],{"class":278},"\"xlrd\"",[268,362,363],{"class":318},"):\n",[268,365,367,370],{"class":270,"line":366},4,[268,368,369],{"class":314},"    try",[268,371,372],{"class":318},":\n",[268,374,376,380],{"class":270,"line":375},5,[268,377,379],{"class":378},"sP0c6","        print",[268,381,382],{"class":318},"(pkg, md.version(pkg))\n",[268,384,386,389],{"class":270,"line":385},6,[268,387,388],{"class":314},"    except",[268,390,391],{"class":318}," md.PackageNotFoundError:\n",[268,393,395,397,400,403],{"class":270,"line":394},7,[268,396,379],{"class":378},[268,398,399],{"class":318},"(pkg, ",[268,401,402],{"class":278},"\"not installed\"",[268,404,405],{"class":318},")\n",[231,407,409],{"id":408},"step-1-reproduce-it-and-read-the-traceback","Step 1 — Reproduce it and read the traceback",[10,411,412],{},"The error comes from two kinds of call, and the traceback tells you which one you have. With pandas, an explicit engine is the usual culprit:",[19,414,416],{"className":305,"code":415,"language":307,"meta":25,"style":25},"import pandas as pd\n\ntry:\n    pd.read_excel(\"orders.xlsx\", engine=\"xlrd\")\nexcept Exception as exc:\n    print(type(exc).__module__, type(exc).__name__, exc)\n# xlrd.biffh XLRDError Excel xlsx file; not supported\n",[14,417,418,430,434,441,462,476,505],{"__ignoreMap":25},[268,419,420,422,425,427],{"class":270,"line":271},[268,421,315],{"class":314},[268,423,424],{"class":318}," pandas ",[268,426,322],{"class":314},[268,428,429],{"class":318}," pd\n",[268,431,432],{"class":270,"line":288},[268,433,331],{"emptyLinePlaceholder":330},[268,435,436,439],{"class":270,"line":334},[268,437,438],{"class":314},"try",[268,440,372],{"class":318},[268,442,443,446,449,451,455,458,460],{"class":270,"line":366},[268,444,445],{"class":318},"    pd.read_excel(",[268,447,448],{"class":278},"\"orders.xlsx\"",[268,450,352],{"class":318},[268,452,454],{"class":453},"sa561","engine",[268,456,457],{"class":314},"=",[268,459,360],{"class":278},[268,461,405],{"class":318},[268,463,464,467,470,473],{"class":270,"line":375},[268,465,466],{"class":314},"except",[268,468,469],{"class":378}," Exception",[268,471,472],{"class":314}," as",[268,474,475],{"class":318}," exc:\n",[268,477,478,481,484,487,490,493,495,497,499,502],{"class":270,"line":385},[268,479,480],{"class":378},"    print",[268,482,483],{"class":318},"(",[268,485,486],{"class":378},"type",[268,488,489],{"class":318},"(exc).",[268,491,492],{"class":378},"__module__",[268,494,352],{"class":318},[268,496,486],{"class":378},[268,498,489],{"class":318},[268,500,501],{"class":378},"__name__",[268,503,504],{"class":318},", exc)\n",[268,506,507],{"class":270,"line":394},[268,508,509],{"class":298},"# xlrd.biffh XLRDError Excel xlsx file; not supported\n",[10,511,512,513,515,516,519],{},"If the bottom of the traceback shows your own code calling ",[14,514,41],{},", the script uses xlrd directly — go to Step 4. If it shows pandas' ",[14,517,518],{},"_xlrd.py"," reader, something told pandas to use xlrd — Step 2.",[231,521,523],{"id":522},"step-2-stop-forcing-the-engine","Step 2 — Stop forcing the engine",[10,525,526,527,529,530,532,533,535],{},"Before pandas 1.2, ",[14,528,16],{}," defaulted to xlrd for every Excel file, so older scripts often spelled the engine out, and tutorials copied it. Today the right choice is to leave ",[14,531,454],{}," unset. pandas reads the file's first bytes, recognises the zip container of an ",[14,534,37],{},", and picks openpyxl:",[19,537,539],{"className":305,"code":538,"language":307,"meta":25,"style":25},"df = pd.read_excel(\"orders.xlsx\")          # engine chosen from the content\nprint(df)\n",[14,540,541,559],{"__ignoreMap":25},[268,542,543,546,548,551,553,556],{"class":270,"line":271},[268,544,545],{"class":318},"df ",[268,547,457],{"class":314},[268,549,550],{"class":318}," pd.read_excel(",[268,552,448],{"class":278},[268,554,555],{"class":318},")          ",[268,557,558],{"class":298},"# engine chosen from the content\n",[268,560,561,564],{"class":270,"line":288},[268,562,563],{"class":378},"print",[268,565,566],{"class":318},"(df)\n",[10,568,569,570,572],{},"Search for every place the engine is set — it hides in more places than ",[14,571,16],{}," itself:",[19,574,576],{"className":305,"code":575,"language":307,"meta":25,"style":25},"import pathlib, re\n\npattern = re.compile(r\"\"\"engine\\s*=\\s*['\"]xlrd['\"]|open_workbook\\(\"\"\")\nfor path in pathlib.Path(\".\").rglob(\"*.py\"):\n    for n, line in enumerate(path.read_text(errors=\"ignore\").splitlines(), 1):\n        if pattern.search(line):\n            print(f\"{path}:{n}: {line.strip()}\")\n",[14,577,578,585,589,650,673,705,713],{"__ignoreMap":25},[268,579,580,582],{"class":270,"line":271},[268,581,315],{"class":314},[268,583,584],{"class":318}," pathlib, re\n",[268,586,587],{"class":270,"line":288},[268,588,331],{"emptyLinePlaceholder":330},[268,590,591,594,596,599,602,605,608,611,613,615,617,620,624,627,629,631,633,635,638,641,645,648],{"class":270,"line":334},[268,592,593],{"class":318},"pattern ",[268,595,457],{"class":314},[268,597,598],{"class":318}," re.compile(",[268,600,601],{"class":314},"r",[268,603,604],{"class":278},"\"\"\"engine",[268,606,607],{"class":378},"\\s",[268,609,610],{"class":314},"*",[268,612,457],{"class":278},[268,614,607],{"class":378},[268,616,610],{"class":314},[268,618,619],{"class":378},"[",[268,621,623],{"class":622},"sSjpA","'\"",[268,625,626],{"class":378},"]",[268,628,171],{"class":278},[268,630,619],{"class":378},[268,632,623],{"class":622},[268,634,626],{"class":378},[268,636,637],{"class":314},"|",[268,639,640],{"class":278},"open_workbook",[268,642,644],{"class":643},"s_b0D","\\(",[268,646,647],{"class":278},"\"\"\"",[268,649,405],{"class":318},[268,651,652,654,657,659,662,665,668,671],{"class":270,"line":366},[268,653,337],{"class":314},[268,655,656],{"class":318}," path ",[268,658,343],{"class":314},[268,660,661],{"class":318}," pathlib.Path(",[268,663,664],{"class":278},"\".\"",[268,666,667],{"class":318},").rglob(",[268,669,670],{"class":278},"\"*.py\"",[268,672,363],{"class":318},[268,674,675,678,681,683,686,689,692,694,697,700,703],{"class":270,"line":375},[268,676,677],{"class":314},"    for",[268,679,680],{"class":318}," n, line ",[268,682,343],{"class":314},[268,684,685],{"class":378}," enumerate",[268,687,688],{"class":318},"(path.read_text(",[268,690,691],{"class":453},"errors",[268,693,457],{"class":314},[268,695,696],{"class":278},"\"ignore\"",[268,698,699],{"class":318},").splitlines(), ",[268,701,702],{"class":378},"1",[268,704,363],{"class":318},[268,706,707,710],{"class":270,"line":385},[268,708,709],{"class":314},"        if",[268,711,712],{"class":318}," pattern.search(line):\n",[268,714,715,718,720,723,726,729,731,734,737,739,742,744,747,749,752,754,756],{"class":270,"line":394},[268,716,717],{"class":378},"            print",[268,719,483],{"class":318},[268,721,722],{"class":314},"f",[268,724,725],{"class":278},"\"",[268,727,728],{"class":622},"{",[268,730,104],{"class":318},[268,732,733],{"class":622},"}",[268,735,736],{"class":278},":",[268,738,728],{"class":622},[268,740,741],{"class":318},"n",[268,743,733],{"class":622},[268,745,746],{"class":278},": ",[268,748,728],{"class":622},[268,750,751],{"class":318},"line.strip()",[268,753,733],{"class":622},[268,755,725],{"class":278},[268,757,405],{"class":318},[10,759,760,761,764,765,768,769,771],{},"Check ",[14,762,763],{},"pd.ExcelFile(..., engine=\"xlrd\")"," calls and configuration files too. A setting like ",[14,766,767],{},"io.excel.xls.reader"," only applies to ",[14,770,33],{}," and is harmless; an engine stored in a job's YAML or passed from a command-line flag is not.",[231,773,775],{"id":774},"step-3-check-for-xlsx-files-wearing-an-xls-extension","Step 3 — Check for .xlsx files wearing an .xls extension",[10,777,778,779,783,784,786,787,789],{},"The error also appears when a file ",[780,781,782],"em",{},"named"," ",[14,785,33],{}," is really an ",[14,788,37],{},". Systems that export spreadsheets sometimes keep the old extension after switching formats, and people rename files by hand. xlrd inspects the bytes, sees a zip archive and refuses, whatever the name says. Look at the signature:",[19,791,793],{"className":305,"code":792,"language":307,"meta":25,"style":25},"def excel_kind(path):\n    with open(path, \"rb\") as fh:\n        head = fh.read(8)\n    if head.startswith(b\"PK\\x03\\x04\"):\n        return \"xlsx-family (zip) — read with openpyxl\"\n    if head.startswith(bytes.fromhex(\"D0CF11E0A1B11AE1\")):\n        return \"legacy .xls (OLE2) — read with xlrd\"\n    return f\"not an Excel workbook: {head!r}\"\n\nprint(excel_kind(\"orders.xlsx\"))\nprint(excel_kind(\"legacy.xls\"))\n",[14,794,795,807,829,844,865,873,891,898,923,928,941],{"__ignoreMap":25},[268,796,797,800,804],{"class":270,"line":271},[268,798,799],{"class":314},"def",[268,801,803],{"class":802},"s_Opv"," excel_kind",[268,805,806],{"class":318},"(path):\n",[268,808,809,812,815,818,821,824,826],{"class":270,"line":288},[268,810,811],{"class":314},"    with",[268,813,814],{"class":378}," open",[268,816,817],{"class":318},"(path, ",[268,819,820],{"class":278},"\"rb\"",[268,822,823],{"class":318},") ",[268,825,322],{"class":314},[268,827,828],{"class":318}," fh:\n",[268,830,831,834,836,839,842],{"class":270,"line":334},[268,832,833],{"class":318},"        head ",[268,835,457],{"class":314},[268,837,838],{"class":318}," fh.read(",[268,840,841],{"class":378},"8",[268,843,405],{"class":318},[268,845,846,849,852,855,858,861,863],{"class":270,"line":366},[268,847,848],{"class":314},"    if",[268,850,851],{"class":318}," head.startswith(",[268,853,854],{"class":314},"b",[268,856,857],{"class":278},"\"PK",[268,859,860],{"class":622},"\\x03\\x04",[268,862,725],{"class":278},[268,864,363],{"class":318},[268,866,867,870],{"class":270,"line":375},[268,868,869],{"class":314},"        return",[268,871,872],{"class":278}," \"xlsx-family (zip) — read with openpyxl\"\n",[268,874,875,877,879,882,885,888],{"class":270,"line":385},[268,876,848],{"class":314},[268,878,851],{"class":318},[268,880,881],{"class":378},"bytes",[268,883,884],{"class":318},".fromhex(",[268,886,887],{"class":278},"\"D0CF11E0A1B11AE1\"",[268,889,890],{"class":318},")):\n",[268,892,893,895],{"class":270,"line":394},[268,894,869],{"class":314},[268,896,897],{"class":278}," \"legacy .xls (OLE2) — read with xlrd\"\n",[268,899,901,904,907,910,912,915,918,920],{"class":270,"line":900},8,[268,902,903],{"class":314},"    return",[268,905,906],{"class":314}," f",[268,908,909],{"class":278},"\"not an Excel workbook: ",[268,911,728],{"class":622},[268,913,914],{"class":318},"head",[268,916,917],{"class":314},"!r",[268,919,733],{"class":622},[268,921,922],{"class":278},"\"\n",[268,924,926],{"class":270,"line":925},9,[268,927,331],{"emptyLinePlaceholder":330},[268,929,931,933,936,938],{"class":270,"line":930},10,[268,932,563],{"class":378},[268,934,935],{"class":318},"(excel_kind(",[268,937,448],{"class":278},[268,939,940],{"class":318},"))\n",[268,942,944,946,948,951],{"class":270,"line":943},11,[268,945,563],{"class":378},[268,947,935],{"class":318},[268,949,950],{"class":278},"\"legacy.xls\"",[268,952,940],{"class":318},[10,954,955,956,958,959,963],{},"A zip signature under an ",[14,957,33],{}," name is fixed by not forcing the engine — pandas' detection goes by content. For good measure, rename the file so people and other tools are not misled. If the bytes are neither signature, the file is something else again — often HTML or CSV saved with an Excel extension — and ",[51,960,962],{"href":961},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-cannot-be-determined-in-pandas\u002F","Fix \"Excel File Format Cannot Be Determined\" in pandas"," covers that case.",[57,965,66,970,66,973,66,976,66,979,66,985,66,991,66,994,66,998,66,1002,66,1005,66,1008,66,1016,66,1020,66,1025,66,1031,66,1036,66,1040,66,1043,66,1046,66,1049,66,1053,66,1055,66,1058,66,1064,66,1067,66,1070,66,1073,66,1076,66,1079,66,1083,66,1086,66,1089,66,1094,66,1098,66,1101,66,1104,66,1106,66,1109,66,1113,66,1116,66,1119,66,1124,66,1129],{"viewBox":966,"role":60,"ariaLabelledBy":967,"xmlns":64,"style":65},"4 0 752 290",[968,969],"xlrd-which-t","xlrd-which-d",[68,971,972],{"id":968},"Finding what sends an .xlsx file to xlrd",[72,974,975],{"id":969},"A decision chart based on the traceback. If pandas was given engine=\"xlrd\", remove the argument. If the code calls xlrd.open_workbook itself, port it to openpyxl. If the file is named .xls but starts with the zip signature PK, it is really an .xlsx: stop forcing the engine and rename it. If it is a genuine OLE2 .xls file, xlrd is correct and must stay installed.",[76,977],{"x":78,"y":79,"width":80,"height":978,"fill":82},"290",[76,980],{"x":981,"y":982,"width":983,"height":984,"rx":95,"fill":96,"stroke":97,"style":98},"231.6","16.0","296.8","75.5",[100,986,988],{"transform":987},"translate(368.0,29.0) scale(1.000)",[104,989],{"d":990,"fill":107,"stroke":108,"style":109},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5",[24,992,993],{"x":85,"y":984,"style":114},"What does the traceback point at?",[270,995],{"x1":85,"y1":996,"x2":85,"y2":997,"stroke":97,"style":98},"91.5","111.5",[270,999],{"x1":1000,"y1":997,"x2":1001,"y2":997,"stroke":97,"style":98},"104.0","656.0",[270,1003],{"x1":1000,"y1":997,"x2":1000,"y2":1004,"stroke":97,"style":98},"157.5",[151,1006],{"points":1007,"fill":154},"104.0,161.5 99.0,152.5 109.0,152.5",[76,1009],{"x":1010,"y":1011,"width":1012,"height":1013,"rx":1014,"fill":82,"stroke":186,"style":1015},"52.7","121.5","102.7","21.0","10","stroke-width:1.2px",[24,1017,45],{"x":1000,"y":1018,"style":1019},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle",[76,1021],{"x":91,"y":1022,"width":1023,"height":1024,"rx":95,"fill":127,"stroke":128,"style":98},"163.5","168.0","110.5",[100,1026,1028],{"transform":1027},"translate(92.0,176.5) scale(1.000)",[104,1029],{"d":1030,"fill":107,"stroke":135,"style":109},"M5 12.5l4.5 4.5L19 7",[24,1032,1035],{"x":1000,"y":1033,"style":1034},"223.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Remove the engine",[24,1037,1039],{"x":1000,"y":1038,"style":119},"242.5","pandas picks openpyxl",[24,1041,1042],{"x":1000,"y":125,"style":119},"itself",[270,1044],{"x1":1045,"y1":997,"x2":1045,"y2":1004,"stroke":97,"style":98},"288.0",[151,1047],{"points":1048,"fill":154},"288.0,161.5 283.0,152.5 293.0,152.5",[76,1050],{"x":1051,"y":1011,"width":1052,"height":1013,"rx":1014,"fill":82,"stroke":186,"style":1015},"218.3","139.3",[24,1054,41],{"x":1045,"y":1018,"style":1019},[76,1056],{"x":1057,"y":1022,"width":1023,"height":1024,"rx":95,"fill":96,"stroke":97,"style":98},"204.0",[100,1059,1061],{"transform":1060},"translate(276.0,176.5) scale(1.000)",[104,1062],{"d":1063,"fill":107,"stroke":108,"style":109},"M9 8l-4 4 4 4 M15 8l4 4-4 4",[24,1065,1066],{"x":1045,"y":1033,"style":114},"Port to openpyxl",[24,1068,1069],{"x":1045,"y":1038,"style":119},"rows, cells and sheets",[24,1071,1072],{"x":1045,"y":125,"style":119},"change names",[270,1074],{"x1":1075,"y1":997,"x2":1075,"y2":1004,"stroke":97,"style":98},"472.0",[151,1077],{"points":1078,"fill":154},"472.0,161.5 467.0,152.5 477.0,152.5",[76,1080],{"x":1081,"y":1011,"width":1082,"height":1013,"rx":1014,"fill":82,"stroke":186,"style":1015},"404.9","134.2",[24,1084,1085],{"x":1075,"y":1018,"style":1019},".xls name, PK bytes",[76,1087],{"x":1088,"y":1022,"width":1023,"height":1024,"rx":95,"fill":158,"stroke":159,"style":98},"388.0",[100,1090,1092],{"transform":1091},"translate(460.0,176.5) scale(1.000)",[104,1093],{"d":165,"fill":107,"stroke":166,"style":109},[24,1095,1097],{"x":1075,"y":1033,"style":1096},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","A renamed .xlsx",[24,1099,1100],{"x":1075,"y":1038,"style":119},"detect by content, then",[24,1102,1103],{"x":1075,"y":125,"style":119},"rename",[270,1105],{"x1":1001,"y1":997,"x2":1001,"y2":1004,"stroke":97,"style":98},[151,1107],{"points":1108,"fill":154},"656.0,161.5 651.0,152.5 661.0,152.5",[76,1110],{"x":1111,"y":1011,"width":1112,"height":1013,"rx":1014,"fill":82,"stroke":186,"style":1015},"606.9","98.3",[24,1114,1115],{"x":1001,"y":1018,"style":1019},"real OLE2 .xls",[76,1117],{"x":1118,"y":1022,"width":1023,"height":1024,"rx":95,"fill":185,"stroke":186,"style":98},"572.0",[100,1120,1122],{"transform":1121},"translate(644.0,185.2) scale(1.000)",[104,1123],{"d":134,"fill":107,"stroke":192,"style":109},[24,1125,1128],{"x":1001,"y":1126,"style":1127},"231.8","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Keep xlrd",[24,1130,1132],{"x":1001,"y":1131,"style":119},"251.2","it still reads legacy .xls",[231,1134,1136],{"id":1135},"step-4-port-direct-xlrd-code-to-openpyxl","Step 4 — Port direct xlrd code to openpyxl",[10,1138,1139],{},"Scripts written against xlrd's own API need more than an engine change, because openpyxl names things differently and counts from 1 rather than 0. Here is typical xlrd-era code:",[19,1141,1144],{"className":1142,"code":1143,"language":24,"meta":25},[22],"book = xlrd.open_workbook(\"orders.xlsx\")\nsheet = book.sheet_by_name(\"Sheet1\")\nfor r in range(1, sheet.nrows):\n    sku = sheet.cell_value(r, 0)\n    qty = sheet.cell_value(r, 1)\n",[14,1145,1143],{"__ignoreMap":25},[10,1147,1148,1149,1152,1153,736],{},"The openpyxl version reads the same values. ",[14,1150,1151],{},"values_only=True"," hands back plain tuples, which is the closest match to xlrd's ",[14,1154,1155],{},"row_values",[19,1157,1159],{"className":305,"code":1158,"language":307,"meta":25,"style":25},"from openpyxl import load_workbook\n\nbook = load_workbook(\"orders.xlsx\", read_only=True, data_only=True)\nsheet = book[\"Sheet1\"]\nfor sku, qty in sheet.iter_rows(min_row=2, values_only=True):\n    print(sku, qty)\nbook.close()\n",[14,1160,1161,1174,1178,1211,1227,1258,1265],{"__ignoreMap":25},[268,1162,1163,1166,1169,1171],{"class":270,"line":271},[268,1164,1165],{"class":314},"from",[268,1167,1168],{"class":318}," openpyxl ",[268,1170,315],{"class":314},[268,1172,1173],{"class":318}," load_workbook\n",[268,1175,1176],{"class":270,"line":288},[268,1177,331],{"emptyLinePlaceholder":330},[268,1179,1180,1183,1185,1188,1190,1192,1195,1197,1200,1202,1205,1207,1209],{"class":270,"line":334},[268,1181,1182],{"class":318},"book ",[268,1184,457],{"class":314},[268,1186,1187],{"class":318}," load_workbook(",[268,1189,448],{"class":278},[268,1191,352],{"class":318},[268,1193,1194],{"class":453},"read_only",[268,1196,457],{"class":314},[268,1198,1199],{"class":378},"True",[268,1201,352],{"class":318},[268,1203,1204],{"class":453},"data_only",[268,1206,457],{"class":314},[268,1208,1199],{"class":378},[268,1210,405],{"class":318},[268,1212,1213,1216,1218,1221,1224],{"class":270,"line":366},[268,1214,1215],{"class":318},"sheet ",[268,1217,457],{"class":314},[268,1219,1220],{"class":318}," book[",[268,1222,1223],{"class":278},"\"Sheet1\"",[268,1225,1226],{"class":318},"]\n",[268,1228,1229,1231,1234,1236,1239,1242,1244,1247,1249,1252,1254,1256],{"class":270,"line":375},[268,1230,337],{"class":314},[268,1232,1233],{"class":318}," sku, qty ",[268,1235,343],{"class":314},[268,1237,1238],{"class":318}," sheet.iter_rows(",[268,1240,1241],{"class":453},"min_row",[268,1243,457],{"class":314},[268,1245,1246],{"class":378},"2",[268,1248,352],{"class":318},[268,1250,1251],{"class":453},"values_only",[268,1253,457],{"class":314},[268,1255,1199],{"class":378},[268,1257,363],{"class":318},[268,1259,1260,1262],{"class":270,"line":385},[268,1261,480],{"class":378},[268,1263,1264],{"class":318},"(sku, qty)\n",[268,1266,1267],{"class":270,"line":394},[268,1268,1269],{"class":318},"book.close()\n",[10,1271,1272],{},"The translations you will need most often:",[57,1274,66,1279,66,1282,66,1285,66,1288,66,1293,66,1297,66,1303,66,1306,66,1310,66,1314,66,1320,66,1322,66,1326,66,1330,66,1332,66,1336,66,1339,66,1343,66,1345,66,1349,66,1351,66,1353,66,1356,66,1360,66,1362,66,1366,66,1368,66,1371,66,1374,66,1378,66,1380,66,1384,66,1386,66,1388,66,1391,66,1395,66,1397,66,1401,66,1403,66,1406,66,1409,66,1413,66,1415,66,1419,66,1421,66,1423,66,1425,66,1429,66,1431,66,1435,66,1437],{"viewBox":1275,"role":60,"ariaLabelledBy":1276,"xmlns":64,"style":65},"4 0 752 310",[1277,1278],"xlrd-map-t","xlrd-map-d",[68,1280,1281],{"id":1277},"xlrd calls and their openpyxl replacements",[72,1283,1284],{"id":1278},"A lookup table. open_workbook becomes load_workbook with read_only and data_only; sheet_by_name becomes indexing the workbook by name; sheet_by_index becomes worksheets[i]; nrows and ncols become max_row and max_column; cell_value(r, c) becomes cell(r+1, c+1).value; row_values(r) becomes iter_rows with values_only; xldate_as_datetime is not needed because dates arrive as datetime objects.",[76,1286],{"x":78,"y":79,"width":80,"height":1287,"fill":82},"310",[76,1289],{"x":91,"y":982,"width":1290,"height":1291,"rx":79,"fill":82,"stroke":186,"style":1292},"186.0","40.0","stroke-width:1px",[76,1294],{"x":1295,"y":982,"width":1296,"height":1291,"rx":79,"fill":96,"stroke":97,"style":1015},"206.0","267.0",[24,1298,1302],{"x":1299,"y":1300,"style":1301},"339.5","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","openpyxl replacement",[76,1304],{"x":1305,"y":982,"width":1296,"height":1291,"rx":79,"fill":96,"stroke":97,"style":1015},"473.0",[24,1307,1309],{"x":1308,"y":1300,"style":1301},"606.5","Index base",[76,1311],{"x":91,"y":1312,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},"56.0","34.0",[24,1315,1319],{"x":1316,"y":1317,"style":1318},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","open_workbook(path)",[76,1321],{"x":1295,"y":1312,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1323,1325],{"x":1299,"y":1324,"style":119},"71.0","load_workbook(path, read_only=True,",[24,1327,1329],{"x":1299,"y":1328,"style":119},"84.0","data_only=True)",[76,1331],{"x":1305,"y":1312,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1333,1335],{"x":1308,"y":1334,"style":119},"77.0","—",[76,1337],{"x":91,"y":1338,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},"90.0",[24,1340,1342],{"x":1316,"y":1341,"style":1318},"112.0","sheet_by_name(n)",[76,1344],{"x":1295,"y":1338,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1346,1348],{"x":1299,"y":1347,"style":119},"111.0","book[n]",[76,1350],{"x":1305,"y":1338,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1352,1335],{"x":1308,"y":1347,"style":119},[76,1354],{"x":91,"y":1355,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},"124.0",[24,1357,1359],{"x":1316,"y":1358,"style":1318},"146.0","sheet_by_index(i)",[76,1361],{"x":1295,"y":1355,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1363,1365],{"x":1299,"y":1364,"style":119},"145.0","book.worksheets[i]",[76,1367],{"x":1305,"y":1355,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1369,1370],{"x":1308,"y":1364,"style":119},"0-based",[76,1372],{"x":91,"y":1373,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},"158.0",[24,1375,1377],{"x":1316,"y":1376,"style":1318},"180.0","nrows \u002F ncols",[76,1379],{"x":1295,"y":1373,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1381,1383],{"x":1299,"y":1382,"style":119},"179.0","max_row \u002F max_column",[76,1385],{"x":1305,"y":1373,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1387,1335],{"x":1308,"y":1382,"style":119},[76,1389],{"x":91,"y":1390,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},"192.0",[24,1392,1394],{"x":1316,"y":1393,"style":1318},"214.0","cell_value(r, c)",[76,1396],{"x":1295,"y":1390,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1398,1400],{"x":1299,"y":1399,"style":119},"213.0","cell(r + 1, c + 1).value",[76,1402],{"x":1305,"y":1390,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1404,1405],{"x":1308,"y":1399,"style":119},"1-based",[76,1407],{"x":91,"y":1408,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},"226.0",[24,1410,1412],{"x":1316,"y":1411,"style":1318},"248.0","row_values(r)",[76,1414],{"x":1295,"y":1408,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1416,1418],{"x":1299,"y":1417,"style":119},"247.0","iter_rows(values_only=True)",[76,1420],{"x":1305,"y":1408,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1422,1405],{"x":1308,"y":1417,"style":119},[76,1424],{"x":91,"y":125,"width":1290,"height":1313,"rx":79,"fill":185,"stroke":186,"style":1292},[24,1426,1428],{"x":1316,"y":1427,"style":1318},"282.0","xldate_as_datetime(v, 0)",[76,1430],{"x":1295,"y":125,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1432,1434],{"x":1299,"y":1433,"style":119},"281.0","not needed: dates arrive as datetime",[76,1436],{"x":1305,"y":125,"width":1296,"height":1313,"rx":79,"fill":82,"stroke":186,"style":1292},[24,1438,1335],{"x":1308,"y":1433,"style":119},[10,1440,1441,1442,1445,1446,1449,1450,1453,1454,1457],{},"Two behaviour differences are worth testing for when you port. ",[14,1443,1444],{},"data_only=True"," gives you cached formula results, the same thing xlrd returned; without it, openpyxl hands you the formula text. And xlrd returned every number as a float, while openpyxl returns integers as ",[14,1447,1448],{},"int"," — code that did ",[14,1451,1452],{},"int(sheet.cell_value(...))"," keeps working, but code that compared types or formatted with ",[14,1455,1456],{},"%f"," may print differently.",[231,1459,1461],{"id":1460},"step-5-keep-xlrd-only-for-real-xls-files","Step 5 — Keep xlrd only for real .xls files",[10,1463,1464,1465,1467],{},"xlrd is still the right tool for legacy ",[14,1466,33],{}," workbooks, and pandas will use it for them automatically. If a job reads a mix of formats, leave the engine unset and let the content decide:",[19,1469,1471],{"className":305,"code":1470,"language":307,"meta":25,"style":25},"for name in (\"orders.xlsx\", \"legacy.xls\"):\n    print(name, pd.read_excel(name).to_dict(\"records\"))\n",[14,1472,1473,1492],{"__ignoreMap":25},[268,1474,1475,1477,1480,1482,1484,1486,1488,1490],{"class":270,"line":271},[268,1476,337],{"class":314},[268,1478,1479],{"class":318}," name ",[268,1481,343],{"class":314},[268,1483,346],{"class":318},[268,1485,448],{"class":278},[268,1487,352],{"class":318},[268,1489,950],{"class":278},[268,1491,363],{"class":318},[268,1493,1494,1496,1499,1502],{"class":270,"line":288},[268,1495,480],{"class":378},[268,1497,1498],{"class":318},"(name, pd.read_excel(name).to_dict(",[268,1500,1501],{"class":278},"\"records\"",[268,1503,940],{"class":318},[10,1505,1506,1507,1509,1510,1512,1513,55],{},"If the job only ever reads ",[14,1508,37],{},", uninstall xlrd from its environment. A missing optional dependency fails loudly with a clear message the day someone feeds it an ",[14,1511,33],{},", which is better than a stale engine argument quietly surviving. For converting a folder of old files once and for all, see ",[51,1514,1516],{"href":1515},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xls-to-xlsx-with-python\u002F","Convert .xls to .xlsx with Python",[231,1518,1520],{"id":1519},"step-6-add-a-test-so-the-engine-cannot-creep-back","Step 6 — Add a test so the engine cannot creep back",[10,1522,1523,1524,1526],{},"Once the script works, pin the behaviour with a small test that writes an ",[14,1525,37],{},", reads it through the same function your job uses, and fails if anything routes it to xlrd again. It runs in a fraction of a second and catches the regression the day someone pastes an old snippet back in:",[19,1528,1530],{"className":305,"code":1529,"language":307,"meta":25,"style":25},"def load_orders(path):\n    return pd.read_excel(path)          # the job's real reading function\n\ndef test_reads_xlsx_without_xlrd(tmp_path):\n    path = tmp_path \u002F \"orders.xlsx\"\n    pd.DataFrame({\"sku\": [\"A-1\"], \"qty\": [4]}).to_excel(path, index=False)\n    frame = load_orders(path)\n    assert frame.to_dict(\"records\") == [{\"sku\": \"A-1\", \"qty\": 4}]\n\nimport pathlib, tempfile\nwith tempfile.TemporaryDirectory() as d:\n    test_reads_xlsx_without_xlrd(pathlib.Path(d))\nprint(\"test passed\")\n",[14,1531,1532,1541,1551,1555,1565,1581,1618,1628,1663,1667,1674,1687,1693],{"__ignoreMap":25},[268,1533,1534,1536,1539],{"class":270,"line":271},[268,1535,799],{"class":314},[268,1537,1538],{"class":802}," load_orders",[268,1540,806],{"class":318},[268,1542,1543,1545,1548],{"class":270,"line":288},[268,1544,903],{"class":314},[268,1546,1547],{"class":318}," pd.read_excel(path)          ",[268,1549,1550],{"class":298},"# the job's real reading function\n",[268,1552,1553],{"class":270,"line":334},[268,1554,331],{"emptyLinePlaceholder":330},[268,1556,1557,1559,1562],{"class":270,"line":366},[268,1558,799],{"class":314},[268,1560,1561],{"class":802}," test_reads_xlsx_without_xlrd",[268,1563,1564],{"class":318},"(tmp_path):\n",[268,1566,1567,1570,1572,1575,1578],{"class":270,"line":375},[268,1568,1569],{"class":318},"    path ",[268,1571,457],{"class":314},[268,1573,1574],{"class":318}," tmp_path ",[268,1576,1577],{"class":314},"\u002F",[268,1579,1580],{"class":278}," \"orders.xlsx\"\n",[268,1582,1583,1586,1589,1592,1595,1598,1601,1603,1605,1608,1611,1613,1616],{"class":270,"line":385},[268,1584,1585],{"class":318},"    pd.DataFrame({",[268,1587,1588],{"class":278},"\"sku\"",[268,1590,1591],{"class":318},": [",[268,1593,1594],{"class":278},"\"A-1\"",[268,1596,1597],{"class":318},"], ",[268,1599,1600],{"class":278},"\"qty\"",[268,1602,1591],{"class":318},[268,1604,78],{"class":378},[268,1606,1607],{"class":318},"]}).to_excel(path, ",[268,1609,1610],{"class":453},"index",[268,1612,457],{"class":314},[268,1614,1615],{"class":378},"False",[268,1617,405],{"class":318},[268,1619,1620,1623,1625],{"class":270,"line":394},[268,1621,1622],{"class":318},"    frame ",[268,1624,457],{"class":314},[268,1626,1627],{"class":318}," load_orders(path)\n",[268,1629,1630,1633,1636,1638,1640,1643,1646,1648,1650,1652,1654,1656,1658,1660],{"class":270,"line":900},[268,1631,1632],{"class":314},"    assert",[268,1634,1635],{"class":318}," frame.to_dict(",[268,1637,1501],{"class":278},[268,1639,823],{"class":318},[268,1641,1642],{"class":314},"==",[268,1644,1645],{"class":318}," [{",[268,1647,1588],{"class":278},[268,1649,746],{"class":318},[268,1651,1594],{"class":278},[268,1653,352],{"class":318},[268,1655,1600],{"class":278},[268,1657,746],{"class":318},[268,1659,78],{"class":378},[268,1661,1662],{"class":318},"}]\n",[268,1664,1665],{"class":270,"line":925},[268,1666,331],{"emptyLinePlaceholder":330},[268,1668,1669,1671],{"class":270,"line":930},[268,1670,315],{"class":314},[268,1672,1673],{"class":318}," pathlib, tempfile\n",[268,1675,1676,1679,1682,1684],{"class":270,"line":943},[268,1677,1678],{"class":314},"with",[268,1680,1681],{"class":318}," tempfile.TemporaryDirectory() ",[268,1683,322],{"class":314},[268,1685,1686],{"class":318}," d:\n",[268,1688,1690],{"class":270,"line":1689},12,[268,1691,1692],{"class":318},"    test_reads_xlsx_without_xlrd(pathlib.Path(d))\n",[268,1694,1696,1698,1700,1703],{"class":270,"line":1695},13,[268,1697,563],{"class":378},[268,1699,483],{"class":318},[268,1701,1702],{"class":278},"\"test passed\"",[268,1704,405],{"class":318},[10,1706,1707,1708,1711,1712,1715,1716,1720],{},"In a real project the test lives in ",[14,1709,1710],{},"tests\u002F"," and pytest supplies ",[14,1713,1714],{},"tmp_path","; the last three lines only run it here. ",[51,1717,1719],{"href":1718},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Ftest-excel-output-with-pytest\u002F","Test Excel Output with pytest"," shows the fuller setup.",[231,1722,1724],{"id":1723},"common-pitfalls","Common pitfalls",[1726,1727,1728,1739,1755,1768,1774],"ul",{},[1729,1730,1731,1738],"li",{},[1732,1733,1734,1735,55],"strong",{},"Pinning ",[14,1736,1737],{},"xlrd==1.2.0"," It silences the error by reinstating a parser the maintainers removed. You inherit its bugs, and pip will eventually refuse to resolve it with a current pandas. Fix the code path instead.",[1729,1740,1741,1748,1749,1751,1752,1754],{},[1732,1742,1743,1744,1747],{},"Setting ",[14,1745,1746],{},"engine=\"openpyxl\""," everywhere."," Better than xlrd for ",[14,1750,37],{},", but it now breaks on genuine ",[14,1753,33],{}," files. Unless you have a reason, let pandas choose.",[1729,1756,1757,1763,1764,1767],{},[1732,1758,1759,1760,1762],{},"Forgetting ",[14,1761,1444],{}," when porting."," Without it, formula cells return strings like ",[14,1765,1766],{},"=SUM(B2:B9)"," where xlrd gave numbers.",[1729,1769,1770,1773],{},[1732,1771,1772],{},"Off-by-one errors."," xlrd rows and columns start at 0; openpyxl's start at 1. Port loops carefully and compare the first and last rows against the old output.",[1729,1775,1776,1779],{},[1732,1777,1778],{},"Assuming the extension is the truth."," Check the first bytes of any file whose origin you do not control.",[231,1781,1783],{"id":1782},"performance-notes","Performance notes",[10,1785,1786,1787,1789,1790,1793,1794,1798],{},"For large ",[14,1788,37],{}," files, ",[14,1791,1792],{},"load_workbook(..., read_only=True)"," streams rows instead of building every cell in memory, which is roughly what xlrd's on-demand mode offered. If reading speed was the reason someone reached for xlrd in the first place, the calamine engine is considerably faster than either — ",[51,1795,1797],{"href":1796},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fspeed-up-pandas-excel-reads-with-the-calamine-engine\u002F","Speed Up pandas Excel Reads with the calamine Engine"," has the numbers.",[231,1800,1802],{"id":1801},"conclusion","Conclusion",[10,1804,1805,1808,1809,1811,1812,1814,1815,1817,1818,1820,1821,1823,1824,1826],{},[14,1806,1807],{},"Excel xlsx file; not supported"," means an ",[14,1810,37],{}," workbook reached xlrd, which since version 2.0 only reads ",[14,1813,33],{},". Remove any ",[14,1816,45],{}," and let pandas detect the format, check the bytes of files whose extension may lie, port direct ",[14,1819,41],{}," code to openpyxl with ",[14,1822,1444],{}," and 1-based indexes, and keep xlrd installed only where genuine ",[14,1825,33],{}," files still arrive.",[231,1828,1830],{"id":1829},"frequently-asked-questions","Frequently asked questions",[10,1832,1833,1836],{},[1732,1834,1835],{},"Can I just install xlrd 1.2.0 to make the error go away?","\nIt works, but it is the wrong fix. xlrd 1.2 is unmaintained for .xlsx, its .xlsx parser was removed partly over XML security concerns, and it will not install cleanly alongside current pandas versions forever. Switch the .xlsx path to openpyxl instead.",[10,1838,1839,1842],{},[1732,1840,1841],{},"Do I still need xlrd at all?","\nOnly for legacy .xls files (Excel 97–2003). pandas uses xlrd automatically for those when it is installed. For .xlsx, .xlsm and .xlsb you do not need it.",[10,1844,1845,1848],{},[1732,1846,1847],{},"Why does the error appear on a file that ends in .xls?","\nThe file is really an .xlsx workbook with the wrong extension. xlrd looks at the bytes, sees a zip container, and refuses. pandas' own format detection gets this right if you stop forcing engine=\"xlrd\".",[10,1850,1851,1854],{},[1732,1852,1853],{},"What replaces xlrd's xldate_as_datetime?","\nNothing is needed with openpyxl. It converts date-formatted cells to Python datetime objects as it reads, so the conversion step disappears.",[231,1856,1858],{"id":1857},"related","Related",[1726,1860,1861,1867,1874,1881],{},[1729,1862,1863,1864,1866],{},"Up one level: ",[51,1865,54],{"href":53}," — the map of read and write errors.",[1729,1868,1869,1873],{},[51,1870,1872],{"href":1871},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-xls-files-in-python-with-xlrd-and-pandas\u002F","Read .xls Files in Python with xlrd and pandas"," — where xlrd still belongs.",[1729,1875,1876,1880],{},[51,1877,1879],{"href":1878},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpick-an-excel-engine-for-xlsx-xlsm-xls-xlsb-and-ods\u002F","Pick an Excel Engine for .xlsx, .xlsm, .xls, .xlsb and .ods"," — the full engine map.",[1729,1882,1883,1887],{},[51,1884,1886],{"href":1885},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-openpyxl-does-not-support-the-old-xls-format\u002F","Fix \"openpyxl does not support the old .xls file format\""," — the mirror-image error.",[1889,1890,1891],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":25,"searchDepth":288,"depth":288,"links":1893},[1894,1895,1896,1897,1898,1899,1900,1901,1902,1903,1904,1905,1906],{"id":233,"depth":288,"text":234},{"id":258,"depth":288,"text":259},{"id":408,"depth":288,"text":409},{"id":522,"depth":288,"text":523},{"id":774,"depth":288,"text":775},{"id":1135,"depth":288,"text":1136},{"id":1460,"depth":288,"text":1461},{"id":1519,"depth":288,"text":1520},{"id":1723,"depth":288,"text":1724},{"id":1782,"depth":288,"text":1783},{"id":1801,"depth":288,"text":1802},{"id":1829,"depth":288,"text":1830},{"id":1857,"depth":288,"text":1858},"2026-09-25","xlrd 2.0 reads only legacy .xls files, so any code path that sends an .xlsx workbook to xlrd now fails. Find where xlrd is being chosen, switch that path to openpyxl, and port old xlrd-style code without pinning an outdated version.","md",[1911,1913,1915,1917],{"q":1835,"a":1912},"It works, but it is the wrong fix. xlrd 1.2 is unmaintained for .xlsx, its .xlsx parser was removed partly over XML security concerns, and it will not install cleanly alongside current pandas versions forever. Switch the .xlsx path to openpyxl instead.",{"q":1841,"a":1914},"Only for legacy .xls files (Excel 97–2003). pandas uses xlrd automatically for those when it is installed. For .xlsx, .xlsm and .xlsb you do not need it.",{"q":1847,"a":1916},"The file is really an .xlsx workbook with the wrong extension. xlrd looks at the bytes, sees a zip container, and refuses. pandas' own format detection gets this right if you stop forcing engine=\"xlrd\".",{"q":1853,"a":1918},"Nothing is needed with openpyxl. It converts date-formatted cells to Python datetime objects as it reads, so the conversion step disappears.","fix xlrderror excel xlsx file not supported",null,{"breadcrumb":1922},[1923,1925,1928],{"name":1924,"item":1577},"Home",{"name":1926,"item":1927},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":54,"item":53},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported",{"title":1931,"description":1932},"Fix xlrd.biffh.XLRDError: Excel xlsx file; not supported","Why pandas and xlrd raise \"Excel xlsx file; not supported\", how to find the code that forces the xlrd engine, and how to port xlrd sheet code to openpyxl.","fix-xlrderror-excel-xlsx-file-not-supported","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlrderror-excel-xlsx-file-not-supported\u002Findex",[1936,1937,1938],"troubleshooting","file-formats","reading-excel","how-to","ngB2Rpfak-IERdR6CzSoO5qDJvR4hXfflZpe2PxpEMw",[1942,1946],{"title":1943,"path":1944,"stem":1945,"children":-1},"Fix Wrong Sheet Order After Writing with pandas","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-wrong-sheet-order-after-writing-with-pandas","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-wrong-sheet-order-after-writing-with-pandas\u002Findex",{"title":1947,"path":1948,"stem":1949,"children":-1},"Fix xlsxwriter \"Exception caught in workbook destructor\"","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlsxwriter-exception-caught-in-workbook-destructor","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-xlsxwriter-exception-caught-in-workbook-destructor\u002Findex",[1951,1953],{"path":1952,"title":1926},"\u002Fgetting-started-with-python-excel-automation",{"path":1954,"title":54},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors",[],[1957,1961,1964,1967,1970,1973],{"path":1958,"title":1959,"kind":1960},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fhandle-permission-denied-when-writing-excel-in-python","Handle Permission Denied When Writing Excel in Python","fix",{"path":1962,"title":1963,"kind":1939},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook","What openpyxl Loses When You Save an Existing Workbook",{"path":1965,"title":1966,"kind":1960},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates","Fix Excel Serial Numbers Showing Instead of Dates",{"path":1968,"title":1969,"kind":1939},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fhandle-daylight-saving-time-in-excel-timestamps","Handle Daylight Saving Time in Excel Timestamps",{"path":1971,"title":1972,"kind":1939},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fhandle-microsoft-graph-throttling-for-excel","Handle Microsoft Graph Throttling in Excel Automation",{"path":1974,"title":1975,"kind":1976},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation","Error Handling and Logging in Excel Automation","guide",1790972067782]