[{"data":1,"prerenderedAt":1957},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-cannot-be-determined-in-pandas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-cannot-be-determined-in-pandas":1948},{"id":4,"title":5,"body":6,"dateModified":1925,"datePublished":1925,"description":1926,"extension":1927,"faq":1928,"meta":1939,"navigation":321,"path":1940,"seo":1941,"slug":1944,"stem":1945,"type":1946,"__hash__":1947},"docs\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-cannot-be-determined-in-pandas\u002Findex.md","Fix \"Excel File Format Cannot Be Determined\" in pandas",{"type":7,"value":8,"toc":1911},"minimark",[9,23,196,201,204,287,291,294,352,359,363,374,653,660,664,671,859,874,947,951,958,1116,1131,1135,1138,1413,1494,1505,1509,1516,1700,1707,1711,1781,1785,1797,1801,1807,1811,1823,1838,1848,1857,1867,1871,1907],[10,11,12,16,17,22],"p",{},[13,14,15],"code",{},"ValueError: Excel file format cannot be determined, you must specify an engine manually"," is pandas telling you it has run out of ways to identify your file. It is not a corruption error and not a missing-package error: pandas looks at the extension, then at the leading bytes, and when neither matches a format it knows, it refuses to guess. This guide covers each situation that produces it — a wrong extension, no extension at all, an in-memory buffer, a downloaded response — and the fix for each. It is one branch of ",[18,19,21],"a",{"href":20},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002F","Troubleshooting Common Python Excel Errors",".",[24,25,33,34,33,38,33,42,33,49,33,56,33,65,33,71,33,76,33,80,33,85,33,90,33,93,33,97,33,101,33,109,33,114,33,118,33,122,33,125,33,128,33,135,33,141,33,145,33,150,33,153,33,157,33,161,33,167,33,171,33,175,33,180,33,184,33,187,33,191],"svg",{"viewBox":26,"role":27,"ariaLabelledBy":28,"xmlns":31,"style":32},"0 0 760 262","img",[29,30],"fmt-t","fmt-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[35,36,37],"title",{"id":29},"How pandas decides which engine to use",[39,40,41],"desc",{"id":30},"read_excel uses an explicit engine argument if given, otherwise the filename extension, otherwise the leading bytes of the stream; when none of those identifies a format it raises the value error.",[43,44],"rect",{"x":45,"y":45,"width":46,"height":47,"fill":48},"0","760","262","#ffffff",[50,51,55],"text",{"x":52,"y":53,"style":54},"380","26","font-size:13px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","read_excel() engine resolution, in order",[43,57],{"x":58,"y":59,"width":60,"height":61,"rx":62,"fill":63,"stroke":64},"40","44","200","58","10","#d9f4f1","var(--line,#cdd5e6)",[50,66,70],{"x":67,"y":68,"style":69},"140","70","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","1. engine= given?",[50,72,75],{"x":67,"y":73,"style":74},"90","font-size:11.5px;fill:var(--teal-ink,#0b6157);text-anchor:middle","use it, stop asking",[43,77],{"x":58,"y":78,"width":60,"height":61,"rx":62,"fill":79,"stroke":64},"116","#ebebfd",[50,81,84],{"x":67,"y":82,"style":83},"142","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","2. filename extension",[50,86,89],{"x":67,"y":87,"style":88},"162","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle",".xlsx .xls .xlsb .ods",[43,91],{"x":58,"y":92,"width":60,"height":61,"rx":62,"fill":79,"stroke":64},"188",[50,94,96],{"x":67,"y":95,"style":83},"214","3. leading bytes",[50,98,100],{"x":67,"y":99,"style":88},"234","PK… or OLE2 header",[102,103],"line",{"x1":104,"y1":105,"x2":106,"y2":105,"stroke":107,"style":108},"240","73","286","var(--teal,#0f9488)","stroke-width:2px",[110,111],"polygon",{"points":112,"fill":113},"286,73 276,68 276,78","#0f766e",[102,115],{"x1":104,"y1":116,"x2":106,"y2":116,"stroke":117,"style":108},"145","var(--brand,#5b5cf0)",[110,119],{"points":120,"fill":121},"286,145 276,140 276,150","#5b5cf0",[102,123],{"x1":104,"y1":124,"x2":106,"y2":124,"stroke":117,"style":108},"217",[110,126],{"points":127,"fill":121},"286,217 276,212 276,222",[43,129],{"x":130,"y":59,"width":131,"height":132,"rx":133,"fill":134,"stroke":64},"290","196","202","12","#f0f2f5",[50,136,140],{"x":137,"y":138,"style":139},"388","130","font-size:13px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","a supported",[50,142,144],{"x":137,"y":143,"style":139},"152","format identified?",[102,146],{"x1":147,"y1":148,"x2":149,"y2":148,"stroke":107,"style":108},"486","100","532",[110,151],{"points":152,"fill":113},"532,100 522,95 522,105",[102,154],{"x1":147,"y1":155,"x2":149,"y2":155,"stroke":156,"style":108},"190","var(--accent,#f43f8f)",[110,158],{"points":159,"fill":160},"532,190 522,185 522,195","#be185d",[50,162,166],{"x":163,"y":164,"style":165},"500","88","font-size:11px;fill:var(--muted,#5b6780);text-anchor:start","yes",[50,168,170],{"x":163,"y":169,"style":165},"178","no",[43,172],{"x":173,"y":174,"width":131,"height":61,"rx":62,"fill":63,"stroke":64},"536","72",[50,176,179],{"x":177,"y":178,"style":69},"634","98","parse the workbook",[50,181,183],{"x":177,"y":182,"style":74},"118","DataFrame returned",[43,185],{"x":173,"y":87,"width":131,"height":61,"rx":62,"fill":186,"stroke":64},"#fee8f2",[50,188,190],{"x":177,"y":92,"style":189},"font-size:12.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","ValueError raised",[50,192,195],{"x":177,"y":193,"style":194},"208","font-size:11.5px;fill:var(--accent-ink,#be185d);text-anchor:middle","\"specify an engine\"",[197,198,200],"h2",{"id":199},"prerequisites","Prerequisites",[10,202,203],{},"pandas plus at least one engine. Which one depends on the formats you actually receive:",[205,206,211],"pre",{"className":207,"code":208,"language":209,"meta":210,"style":210},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl        # .xlsx and .xlsm\npip install xlrd                   # legacy .xls\npip install pyxlsb                 # binary .xlsb\npip install odfpy                  # OpenDocument .ods\npip install python-calamine        # all of the above, one engine\n","bash","",[13,212,213,235,248,261,274],{"__ignoreMap":210},[214,215,217,221,225,228,231],"span",{"class":102,"line":216},1,[214,218,220],{"class":219},"sMTad","pip",[214,222,224],{"class":223},"srMev"," install",[214,226,227],{"class":223}," pandas",[214,229,230],{"class":223}," openpyxl",[214,232,234],{"class":233},"s-wDw","        # .xlsx and .xlsm\n",[214,236,238,240,242,245],{"class":102,"line":237},2,[214,239,220],{"class":219},[214,241,224],{"class":223},[214,243,244],{"class":223}," xlrd",[214,246,247],{"class":233},"                   # legacy .xls\n",[214,249,251,253,255,258],{"class":102,"line":250},3,[214,252,220],{"class":219},[214,254,224],{"class":223},[214,256,257],{"class":223}," pyxlsb",[214,259,260],{"class":233},"                 # binary .xlsb\n",[214,262,264,266,268,271],{"class":102,"line":263},4,[214,265,220],{"class":219},[214,267,224],{"class":223},[214,269,270],{"class":223}," odfpy",[214,272,273],{"class":233},"                  # OpenDocument .ods\n",[214,275,277,279,281,284],{"class":102,"line":276},5,[214,278,220],{"class":219},[214,280,224],{"class":223},[214,282,283],{"class":223}," python-calamine",[214,285,286],{"class":233},"        # all of the above, one engine\n",[197,288,290],{"id":289},"the-one-line-fix-and-why-it-works","The one-line fix, and why it works",[10,292,293],{},"Name the engine. It overrides detection entirely, so the extension becomes irrelevant:",[205,295,299],{"className":296,"code":297,"language":298,"meta":210,"style":210},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ndf = pd.read_excel(\"export.dat\", engine=\"openpyxl\")\n","python",[13,300,301,317,323],{"__ignoreMap":210},[214,302,303,307,311,314],{"class":102,"line":216},[214,304,306],{"class":305},"s-kum","import",[214,308,310],{"class":309},"skGVy"," pandas ",[214,312,313],{"class":305},"as",[214,315,316],{"class":309}," pd\n",[214,318,319],{"class":102,"line":237},[214,320,322],{"emptyLinePlaceholder":321},true,"\n",[214,324,325,328,331,334,337,340,344,346,349],{"class":102,"line":250},[214,326,327],{"class":309},"df ",[214,329,330],{"class":305},"=",[214,332,333],{"class":309}," pd.read_excel(",[214,335,336],{"class":223},"\"export.dat\"",[214,338,339],{"class":309},", ",[214,341,343],{"class":342},"sa561","engine",[214,345,330],{"class":305},[214,347,348],{"class":223},"\"openpyxl\"",[214,350,351],{"class":309},")\n",[10,353,354,355,358],{},"That is the whole fix when you already know the format. The interesting cases are the ones where you do not — a file arriving from a system that names things ",[13,356,357],{},"download",", or an HTTP response body with no filename anywhere in sight.",[197,360,362],{"id":361},"files-with-no-extension-and-files-with-the-wrong-one","Files with no extension, and files with the wrong one",[10,364,365,366,369,370,373],{},"If a file has no extension, pandas falls back to inspecting the bytes, which works for genuine ",[13,367,368],{},".xlsx"," and ",[13,371,372],{},".xls"," files. It fails when the content is not a spreadsheet at all — an HTML table or a CSV — because no engine can read those. Identify first, then dispatch:",[205,375,377],{"className":296,"code":376,"language":298,"meta":210,"style":210},"\"\"\"Read a spreadsheet whose extension tells you nothing.\"\"\"\nfrom pathlib import Path\n\nimport pandas as pd\n\ndef read_unknown(path: str) -> pd.DataFrame:\n    head = Path(path).read_bytes()[:8]\n    if head[:2] == b\"PK\":\n        return pd.read_excel(path, engine=\"openpyxl\")\n    if head[:4] == b\"\\xd0\\xcf\\x11\\xe0\":\n        return pd.read_excel(path, engine=\"xlrd\")\n    if head[:5].lower() in (b\"\u003Chtml\", b\"\u003C!doc\"):\n        return pd.read_html(path)[0]\n    return pd.read_csv(path, sep=None, engine=\"python\", encoding=\"utf-8-sig\")\n\ndf = read_unknown(\"downloads\u002Fexport\")\nprint(df.shape)\n",[13,378,379,384,397,401,411,415,435,452,479,496,523,539,574,586,624,629,644],{"__ignoreMap":210},[214,380,381],{"class":102,"line":216},[214,382,383],{"class":223},"\"\"\"Read a spreadsheet whose extension tells you nothing.\"\"\"\n",[214,385,386,389,392,394],{"class":102,"line":237},[214,387,388],{"class":305},"from",[214,390,391],{"class":309}," pathlib ",[214,393,306],{"class":305},[214,395,396],{"class":309}," Path\n",[214,398,399],{"class":102,"line":250},[214,400,322],{"emptyLinePlaceholder":321},[214,402,403,405,407,409],{"class":102,"line":263},[214,404,306],{"class":305},[214,406,310],{"class":309},[214,408,313],{"class":305},[214,410,316],{"class":309},[214,412,413],{"class":102,"line":276},[214,414,322],{"emptyLinePlaceholder":321},[214,416,418,421,425,428,432],{"class":102,"line":417},6,[214,419,420],{"class":305},"def",[214,422,424],{"class":423},"s_Opv"," read_unknown",[214,426,427],{"class":309},"(path: ",[214,429,431],{"class":430},"sP0c6","str",[214,433,434],{"class":309},") -> pd.DataFrame:\n",[214,436,438,441,443,446,449],{"class":102,"line":437},7,[214,439,440],{"class":309},"    head ",[214,442,330],{"class":305},[214,444,445],{"class":309}," Path(path).read_bytes()[:",[214,447,448],{"class":430},"8",[214,450,451],{"class":309},"]\n",[214,453,455,458,461,464,467,470,473,476],{"class":102,"line":454},8,[214,456,457],{"class":305},"    if",[214,459,460],{"class":309}," head[:",[214,462,463],{"class":430},"2",[214,465,466],{"class":309},"] ",[214,468,469],{"class":305},"==",[214,471,472],{"class":305}," b",[214,474,475],{"class":223},"\"PK\"",[214,477,478],{"class":309},":\n",[214,480,482,485,488,490,492,494],{"class":102,"line":481},9,[214,483,484],{"class":305},"        return",[214,486,487],{"class":309}," pd.read_excel(path, ",[214,489,343],{"class":342},[214,491,330],{"class":305},[214,493,348],{"class":223},[214,495,351],{"class":309},[214,497,499,501,503,506,508,510,512,515,519,521],{"class":102,"line":498},10,[214,500,457],{"class":305},[214,502,460],{"class":309},[214,504,505],{"class":430},"4",[214,507,466],{"class":309},[214,509,469],{"class":305},[214,511,472],{"class":305},[214,513,514],{"class":223},"\"",[214,516,518],{"class":517},"sSjpA","\\xd0\\xcf\\x11\\xe0",[214,520,514],{"class":223},[214,522,478],{"class":309},[214,524,526,528,530,532,534,537],{"class":102,"line":525},11,[214,527,484],{"class":305},[214,529,487],{"class":309},[214,531,343],{"class":342},[214,533,330],{"class":305},[214,535,536],{"class":223},"\"xlrd\"",[214,538,351],{"class":309},[214,540,542,544,546,549,552,555,558,561,564,566,568,571],{"class":102,"line":541},12,[214,543,457],{"class":305},[214,545,460],{"class":309},[214,547,548],{"class":430},"5",[214,550,551],{"class":309},"].lower() ",[214,553,554],{"class":305},"in",[214,556,557],{"class":309}," (",[214,559,560],{"class":305},"b",[214,562,563],{"class":223},"\"\u003Chtml\"",[214,565,339],{"class":309},[214,567,560],{"class":305},[214,569,570],{"class":223},"\"\u003C!doc\"",[214,572,573],{"class":309},"):\n",[214,575,577,579,582,584],{"class":102,"line":576},13,[214,578,484],{"class":305},[214,580,581],{"class":309}," pd.read_html(path)[",[214,583,45],{"class":430},[214,585,451],{"class":309},[214,587,589,592,595,598,600,603,605,607,609,612,614,617,619,622],{"class":102,"line":588},14,[214,590,591],{"class":305},"    return",[214,593,594],{"class":309}," pd.read_csv(path, ",[214,596,597],{"class":342},"sep",[214,599,330],{"class":305},[214,601,602],{"class":430},"None",[214,604,339],{"class":309},[214,606,343],{"class":342},[214,608,330],{"class":305},[214,610,611],{"class":223},"\"python\"",[214,613,339],{"class":309},[214,615,616],{"class":342},"encoding",[214,618,330],{"class":305},[214,620,621],{"class":223},"\"utf-8-sig\"",[214,623,351],{"class":309},[214,625,627],{"class":102,"line":626},15,[214,628,322],{"emptyLinePlaceholder":321},[214,630,632,634,636,639,642],{"class":102,"line":631},16,[214,633,327],{"class":309},[214,635,330],{"class":305},[214,637,638],{"class":309}," read_unknown(",[214,640,641],{"class":223},"\"downloads\u002Fexport\"",[214,643,351],{"class":309},[214,645,647,650],{"class":102,"line":646},17,[214,648,649],{"class":430},"print",[214,651,652],{"class":309},"(df.shape)\n",[10,654,655,656,659],{},"Four branches cover essentially every file a business system will hand you. The ",[13,657,658],{},"sep=None, engine=\"python\""," combination lets pandas sniff the delimiter, which matters because \"CSV\" exports are frequently semicolon- or tab-separated in European locales.",[197,661,663],{"id":662},"buffers-downloads-and-streams","Buffers, downloads and streams",[10,665,666,667,670],{},"The most frequent modern cause is an in-memory buffer. A ",[13,668,669],{},"BytesIO"," has no name, so the extension route does not exist and pandas will only work from the leading bytes — which fail the moment the response is an error page rather than a workbook:",[205,672,674],{"className":296,"code":673,"language":298,"meta":210,"style":210},"\"\"\"Download a workbook and read it without touching disk.\"\"\"\nimport io\n\nimport pandas as pd\nimport requests\n\nresp = requests.get(\"https:\u002F\u002Fexample.com\u002Freports\u002Flatest.xlsx\", timeout=30)\nresp.raise_for_status()\n\nctype = resp.headers.get(\"Content-Type\", \"\")\nif \"spreadsheet\" not in ctype and \"excel\" not in ctype:\n    raise ValueError(f\"unexpected content type {ctype!r} — probably an error page\")\n\ndf = pd.read_excel(io.BytesIO(resp.content), engine=\"openpyxl\")\nprint(df.head())\n",[13,675,676,681,688,692,702,709,713,738,743,747,767,797,831,835,852],{"__ignoreMap":210},[214,677,678],{"class":102,"line":216},[214,679,680],{"class":223},"\"\"\"Download a workbook and read it without touching disk.\"\"\"\n",[214,682,683,685],{"class":102,"line":237},[214,684,306],{"class":305},[214,686,687],{"class":309}," io\n",[214,689,690],{"class":102,"line":250},[214,691,322],{"emptyLinePlaceholder":321},[214,693,694,696,698,700],{"class":102,"line":263},[214,695,306],{"class":305},[214,697,310],{"class":309},[214,699,313],{"class":305},[214,701,316],{"class":309},[214,703,704,706],{"class":102,"line":276},[214,705,306],{"class":305},[214,707,708],{"class":309}," requests\n",[214,710,711],{"class":102,"line":417},[214,712,322],{"emptyLinePlaceholder":321},[214,714,715,718,720,723,726,728,731,733,736],{"class":102,"line":437},[214,716,717],{"class":309},"resp ",[214,719,330],{"class":305},[214,721,722],{"class":309}," requests.get(",[214,724,725],{"class":223},"\"https:\u002F\u002Fexample.com\u002Freports\u002Flatest.xlsx\"",[214,727,339],{"class":309},[214,729,730],{"class":342},"timeout",[214,732,330],{"class":305},[214,734,735],{"class":430},"30",[214,737,351],{"class":309},[214,739,740],{"class":102,"line":454},[214,741,742],{"class":309},"resp.raise_for_status()\n",[214,744,745],{"class":102,"line":481},[214,746,322],{"emptyLinePlaceholder":321},[214,748,749,752,754,757,760,762,765],{"class":102,"line":498},[214,750,751],{"class":309},"ctype ",[214,753,330],{"class":305},[214,755,756],{"class":309}," resp.headers.get(",[214,758,759],{"class":223},"\"Content-Type\"",[214,761,339],{"class":309},[214,763,764],{"class":223},"\"\"",[214,766,351],{"class":309},[214,768,769,772,775,778,781,784,787,790,792,794],{"class":102,"line":525},[214,770,771],{"class":305},"if",[214,773,774],{"class":223}," \"spreadsheet\"",[214,776,777],{"class":305}," not",[214,779,780],{"class":305}," in",[214,782,783],{"class":309}," ctype ",[214,785,786],{"class":305},"and",[214,788,789],{"class":223}," \"excel\"",[214,791,777],{"class":305},[214,793,780],{"class":305},[214,795,796],{"class":309}," ctype:\n",[214,798,799,802,805,808,811,814,817,820,823,826,829],{"class":102,"line":541},[214,800,801],{"class":305},"    raise",[214,803,804],{"class":430}," ValueError",[214,806,807],{"class":309},"(",[214,809,810],{"class":305},"f",[214,812,813],{"class":223},"\"unexpected content type ",[214,815,816],{"class":517},"{",[214,818,819],{"class":309},"ctype",[214,821,822],{"class":305},"!r",[214,824,825],{"class":517},"}",[214,827,828],{"class":223}," — probably an error page\"",[214,830,351],{"class":309},[214,832,833],{"class":102,"line":576},[214,834,322],{"emptyLinePlaceholder":321},[214,836,837,839,841,844,846,848,850],{"class":102,"line":588},[214,838,327],{"class":309},[214,840,330],{"class":305},[214,842,843],{"class":309}," pd.read_excel(io.BytesIO(resp.content), ",[214,845,343],{"class":342},[214,847,330],{"class":305},[214,849,348],{"class":223},[214,851,351],{"class":309},[214,853,854,856],{"class":102,"line":626},[214,855,649],{"class":430},[214,857,858],{"class":309},"(df.head())\n",[10,860,861,862,865,866,869,870,22],{},"Two guards make that reliable: ",[13,863,864],{},"raise_for_status()"," turns a 404 into an exception rather than a DataFrame attempt, and the ",[13,867,868],{},"Content-Type"," check catches the login page that many portals return to an unauthenticated request. Reading straight from bytes is covered in more depth in ",[18,871,873],{"href":872},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-an-excel-file-from-a-url-or-bytes-in-python\u002F","Read an Excel file from a URL or bytes in Python",[24,875,33,880,33,883,33,886,33,889,33,893,33,897,33,901,33,907,33,913,33,915,33,919,33,922,33,927,33,930,33,935,33,937,33,940,33,943],{"viewBox":876,"role":27,"ariaLabelledBy":877,"xmlns":31,"style":32},"0 0 760 224",[878,879],"fmt2-t","fmt2-d",[35,881,882],{"id":878},"Why an in-memory buffer loses the format hint",[39,884,885],{"id":879},"Reading from a path gives pandas both a filename and bytes, while reading from BytesIO gives it bytes only, so an explicit engine argument replaces the missing filename hint.",[43,887],{"x":45,"y":45,"width":46,"height":888,"fill":48},"224",[50,890,892],{"x":131,"y":891,"style":83},"28","from a path",[50,894,896],{"x":895,"y":891,"style":189},"566","from a buffer",[102,898],{"x1":52,"y1":58,"x2":52,"y2":899,"stroke":64,"style":900},"206","stroke-width:1px",[43,902],{"x":903,"y":904,"width":905,"height":904,"rx":906,"fill":79,"stroke":64},"34","46","322","9",[50,908,912],{"x":909,"y":910,"style":911},"195","74","font-size:12px;fill:var(--text,#172033);text-anchor:middle","name: sales.xlsx",[43,914],{"x":903,"y":148,"width":905,"height":904,"rx":906,"fill":79,"stroke":64},[50,916,918],{"x":909,"y":917,"style":911},"128","bytes: PK…",[43,920],{"x":903,"y":921,"width":905,"height":904,"rx":906,"fill":63,"stroke":64},"154",[50,923,926],{"x":909,"y":924,"style":925},"182","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","two hints — detection succeeds",[43,928],{"x":929,"y":904,"width":905,"height":904,"rx":906,"fill":134,"stroke":64},"404",[50,931,934],{"x":932,"y":910,"style":933},"565","font-size:12px;fill:var(--muted,#5b6780);text-anchor:middle","name: none",[43,936],{"x":929,"y":148,"width":905,"height":904,"rx":906,"fill":79,"stroke":64},[50,938,939],{"x":932,"y":917,"style":911},"bytes: whatever arrived",[43,941],{"x":929,"y":921,"width":905,"height":904,"rx":906,"fill":942,"stroke":64},"#fdefd8",[50,944,946],{"x":932,"y":924,"style":945},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","pass engine= to replace the hint",[197,948,950],{"id":949},"the-message-that-names-a-missing-package","The message that names a missing package",[10,952,953,954,957],{},"A different error text — ",[13,955,956],{},"Missing optional dependency 'openpyxl'. Use pip or conda to install openpyxl."," — means detection worked fine and the reader is simply not installed. That is a requirements problem, and it is worth failing early rather than at 06:00:",[205,959,961],{"className":296,"code":960,"language":298,"meta":210,"style":210},"\"\"\"Fail at start-up with an actionable message, not mid-report.\"\"\"\nimport importlib\n\nREQUIRED_ENGINES = {\"openpyxl\": \".xlsx\", \"xlrd\": \".xls\"}\n\nmissing = [pkg for pkg in REQUIRED_ENGINES if importlib.util.find_spec(pkg) is None]\nif missing:\n    raise SystemExit(f\"pip install {' '.join(missing)} — needed for \"\n                     f\"{[REQUIRED_ENGINES[m] for m in missing]}\")\n",[13,962,963,968,975,979,1010,1014,1049,1056,1083],{"__ignoreMap":210},[214,964,965],{"class":102,"line":216},[214,966,967],{"class":223},"\"\"\"Fail at start-up with an actionable message, not mid-report.\"\"\"\n",[214,969,970,972],{"class":102,"line":237},[214,971,306],{"class":305},[214,973,974],{"class":309}," importlib\n",[214,976,977],{"class":102,"line":250},[214,978,322],{"emptyLinePlaceholder":321},[214,980,981,984,987,990,992,995,998,1000,1002,1004,1007],{"class":102,"line":263},[214,982,983],{"class":430},"REQUIRED_ENGINES",[214,985,986],{"class":305}," =",[214,988,989],{"class":309}," {",[214,991,348],{"class":223},[214,993,994],{"class":309},": ",[214,996,997],{"class":223},"\".xlsx\"",[214,999,339],{"class":309},[214,1001,536],{"class":223},[214,1003,994],{"class":309},[214,1005,1006],{"class":223},"\".xls\"",[214,1008,1009],{"class":309},"}\n",[214,1011,1012],{"class":102,"line":276},[214,1013,322],{"emptyLinePlaceholder":321},[214,1015,1016,1019,1021,1024,1027,1030,1032,1035,1038,1041,1044,1047],{"class":102,"line":417},[214,1017,1018],{"class":309},"missing ",[214,1020,330],{"class":305},[214,1022,1023],{"class":309}," [pkg ",[214,1025,1026],{"class":305},"for",[214,1028,1029],{"class":309}," pkg ",[214,1031,554],{"class":305},[214,1033,1034],{"class":430}," REQUIRED_ENGINES",[214,1036,1037],{"class":305}," if",[214,1039,1040],{"class":309}," importlib.util.find_spec(pkg) ",[214,1042,1043],{"class":305},"is",[214,1045,1046],{"class":430}," None",[214,1048,451],{"class":309},[214,1050,1051,1053],{"class":102,"line":437},[214,1052,771],{"class":305},[214,1054,1055],{"class":309}," missing:\n",[214,1057,1058,1060,1063,1065,1067,1070,1072,1075,1078,1080],{"class":102,"line":454},[214,1059,801],{"class":305},[214,1061,1062],{"class":430}," SystemExit",[214,1064,807],{"class":309},[214,1066,810],{"class":305},[214,1068,1069],{"class":223},"\"pip install ",[214,1071,816],{"class":517},[214,1073,1074],{"class":223},"' '",[214,1076,1077],{"class":309},".join(missing)",[214,1079,825],{"class":517},[214,1081,1082],{"class":223}," — needed for \"\n",[214,1084,1085,1088,1090,1092,1095,1097,1100,1102,1105,1107,1110,1112,1114],{"class":102,"line":481},[214,1086,1087],{"class":305},"                     f",[214,1089,514],{"class":223},[214,1091,816],{"class":517},[214,1093,1094],{"class":309},"[",[214,1096,983],{"class":430},[214,1098,1099],{"class":309},"[m] ",[214,1101,1026],{"class":305},[214,1103,1104],{"class":309}," m ",[214,1106,554],{"class":305},[214,1108,1109],{"class":309}," missing]",[214,1111,825],{"class":517},[214,1113,514],{"class":223},[214,1115,351],{"class":309},[10,1117,1118,1119,1122,1123,1126,1127,1130],{},"Container images are the usual culprit: a slim base image plus ",[13,1120,1121],{},"pip install pandas"," gives you pandas with no Excel engine at all, because the engines are optional extras. Installing ",[13,1124,1125],{},"pandas[excel]"," or listing the engines explicitly in ",[13,1128,1129],{},"requirements.txt"," avoids it.",[197,1132,1134],{"id":1133},"validate-user-uploads-before-pandas-ever-sees-them","Validate user uploads before pandas ever sees them",[10,1136,1137],{},"When the file comes from a person rather than a system, the format is genuinely unknown and this error becomes a routine event rather than a bug. Decide what you accept, check it explicitly, and return a message the uploader can act on:",[205,1139,1141],{"className":296,"code":1140,"language":298,"meta":210,"style":210},"\"\"\"Classify an upload before choosing a reader.\"\"\"\nACCEPTED = {\n    b\"PK\\x03\\x04\": (\"xlsx\", \"openpyxl\"),\n    b\"\\xd0\\xcf\\x11\\xe0\": (\"xls\", \"xlrd\"),\n}\n\ndef classify(blob: bytes) -> tuple[str, str | None]:\n    for magic, (kind, engine) in ACCEPTED.items():\n        if blob.startswith(magic):\n            return kind, engine\n    if blob[:5].lower() in (b\"\u003Chtml\", b\"\u003C!doc\"):\n        return \"html\", None\n    if b\",\" in blob[:200] or b\";\" in blob[:200]:\n        return \"csv\", None\n    return \"unknown\", None\n\nkind, engine = classify(open(\"upload.bin\", \"rb\").read(512))\nprint(kind, engine)\n",[13,1142,1143,1148,1158,1184,1205,1209,1213,1243,1259,1267,1275,1302,1314,1347,1358,1369,1373,1405],{"__ignoreMap":210},[214,1144,1145],{"class":102,"line":216},[214,1146,1147],{"class":223},"\"\"\"Classify an upload before choosing a reader.\"\"\"\n",[214,1149,1150,1153,1155],{"class":102,"line":237},[214,1151,1152],{"class":430},"ACCEPTED",[214,1154,986],{"class":305},[214,1156,1157],{"class":309}," {\n",[214,1159,1160,1163,1166,1169,1171,1174,1177,1179,1181],{"class":102,"line":250},[214,1161,1162],{"class":305},"    b",[214,1164,1165],{"class":223},"\"PK",[214,1167,1168],{"class":517},"\\x03\\x04",[214,1170,514],{"class":223},[214,1172,1173],{"class":309},": (",[214,1175,1176],{"class":223},"\"xlsx\"",[214,1178,339],{"class":309},[214,1180,348],{"class":223},[214,1182,1183],{"class":309},"),\n",[214,1185,1186,1188,1190,1192,1194,1196,1199,1201,1203],{"class":102,"line":263},[214,1187,1162],{"class":305},[214,1189,514],{"class":223},[214,1191,518],{"class":517},[214,1193,514],{"class":223},[214,1195,1173],{"class":309},[214,1197,1198],{"class":223},"\"xls\"",[214,1200,339],{"class":309},[214,1202,536],{"class":223},[214,1204,1183],{"class":309},[214,1206,1207],{"class":102,"line":276},[214,1208,1009],{"class":309},[214,1210,1211],{"class":102,"line":417},[214,1212,322],{"emptyLinePlaceholder":321},[214,1214,1215,1217,1220,1223,1226,1229,1231,1233,1235,1238,1240],{"class":102,"line":437},[214,1216,420],{"class":305},[214,1218,1219],{"class":423}," classify",[214,1221,1222],{"class":309},"(blob: ",[214,1224,1225],{"class":430},"bytes",[214,1227,1228],{"class":309},") -> tuple[",[214,1230,431],{"class":430},[214,1232,339],{"class":309},[214,1234,431],{"class":430},[214,1236,1237],{"class":305}," |",[214,1239,1046],{"class":430},[214,1241,1242],{"class":309},"]:\n",[214,1244,1245,1248,1251,1253,1256],{"class":102,"line":454},[214,1246,1247],{"class":305},"    for",[214,1249,1250],{"class":309}," magic, (kind, engine) ",[214,1252,554],{"class":305},[214,1254,1255],{"class":430}," ACCEPTED",[214,1257,1258],{"class":309},".items():\n",[214,1260,1261,1264],{"class":102,"line":481},[214,1262,1263],{"class":305},"        if",[214,1265,1266],{"class":309}," blob.startswith(magic):\n",[214,1268,1269,1272],{"class":102,"line":498},[214,1270,1271],{"class":305},"            return",[214,1273,1274],{"class":309}," kind, engine\n",[214,1276,1277,1279,1282,1284,1286,1288,1290,1292,1294,1296,1298,1300],{"class":102,"line":525},[214,1278,457],{"class":305},[214,1280,1281],{"class":309}," blob[:",[214,1283,548],{"class":430},[214,1285,551],{"class":309},[214,1287,554],{"class":305},[214,1289,557],{"class":309},[214,1291,560],{"class":305},[214,1293,563],{"class":223},[214,1295,339],{"class":309},[214,1297,560],{"class":305},[214,1299,570],{"class":223},[214,1301,573],{"class":309},[214,1303,1304,1306,1309,1311],{"class":102,"line":541},[214,1305,484],{"class":305},[214,1307,1308],{"class":223}," \"html\"",[214,1310,339],{"class":309},[214,1312,1313],{"class":430},"None\n",[214,1315,1316,1318,1320,1323,1325,1327,1329,1331,1334,1336,1339,1341,1343,1345],{"class":102,"line":576},[214,1317,457],{"class":305},[214,1319,472],{"class":305},[214,1321,1322],{"class":223},"\",\"",[214,1324,780],{"class":305},[214,1326,1281],{"class":309},[214,1328,60],{"class":430},[214,1330,466],{"class":309},[214,1332,1333],{"class":305},"or",[214,1335,472],{"class":305},[214,1337,1338],{"class":223},"\";\"",[214,1340,780],{"class":305},[214,1342,1281],{"class":309},[214,1344,60],{"class":430},[214,1346,1242],{"class":309},[214,1348,1349,1351,1354,1356],{"class":102,"line":588},[214,1350,484],{"class":305},[214,1352,1353],{"class":223}," \"csv\"",[214,1355,339],{"class":309},[214,1357,1313],{"class":430},[214,1359,1360,1362,1365,1367],{"class":102,"line":626},[214,1361,591],{"class":305},[214,1363,1364],{"class":223}," \"unknown\"",[214,1366,339],{"class":309},[214,1368,1313],{"class":430},[214,1370,1371],{"class":102,"line":631},[214,1372,322],{"emptyLinePlaceholder":321},[214,1374,1375,1378,1380,1383,1386,1388,1391,1393,1396,1399,1402],{"class":102,"line":646},[214,1376,1377],{"class":309},"kind, engine ",[214,1379,330],{"class":305},[214,1381,1382],{"class":309}," classify(",[214,1384,1385],{"class":430},"open",[214,1387,807],{"class":309},[214,1389,1390],{"class":223},"\"upload.bin\"",[214,1392,339],{"class":309},[214,1394,1395],{"class":223},"\"rb\"",[214,1397,1398],{"class":309},").read(",[214,1400,1401],{"class":430},"512",[214,1403,1404],{"class":309},"))\n",[214,1406,1408,1410],{"class":102,"line":1407},18,[214,1409,649],{"class":430},[214,1411,1412],{"class":309},"(kind, engine)\n",[24,1414,33,1419,33,1422,33,1425,33,1428,33,1431,33,1435,33,1439,33,1443,33,1446,33,1449,33,1452,33,1456,33,1459,33,1464,33,1467,33,1469,33,1472,33,1477,33,1482,33,1485,33,1490],{"viewBox":1415,"role":27,"ariaLabelledBy":1416,"xmlns":31,"style":32},"0 0 760 210",[1417,1418],"fmt3-t","fmt3-d",[35,1420,1421],{"id":1417},"An upload gate that answers with a message, not a stack trace",[39,1423,1424],{"id":1418},"Uploads are classified by their leading bytes into workbook, tabular text, or rejected, so the person uploading gets a specific message instead of a pandas value error.",[43,1426],{"x":45,"y":45,"width":46,"height":1427,"fill":48},"210",[50,1429,1430],{"x":52,"y":53,"style":54},"Classify first, read second",[43,1432],{"x":903,"y":904,"width":1433,"height":1434,"rx":133,"fill":134,"stroke":64},"168","120",[50,1436,1438],{"x":182,"y":178,"style":1437},"font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","uploaded file",[50,1440,1442],{"x":182,"y":1441,"style":88},"122","any name, any type",[102,1444],{"x1":132,"y1":1445,"x2":104,"y2":1445,"stroke":117,"style":108},"106",[110,1447],{"points":1448,"fill":121},"240,106 230,101 230,111",[43,1450],{"x":1451,"y":904,"width":1433,"height":1434,"rx":133,"fill":79,"stroke":64},"244",[50,1453,1455],{"x":1454,"y":178,"style":83},"328","first 512 bytes",[50,1457,1458],{"x":1454,"y":1441,"style":88},"matched to a signature",[102,1460],{"x1":1461,"y1":1462,"x2":1463,"y2":1462,"stroke":107,"style":108},"412","82","450",[110,1465],{"points":1466,"fill":113},"450,82 440,77 440,87",[102,1468],{"x1":1461,"y1":138,"x2":1463,"y2":138,"stroke":156,"style":108},[110,1470],{"points":1471,"fill":160},"450,130 440,125 440,135",[43,1473],{"x":1474,"y":904,"width":1475,"height":1476,"rx":62,"fill":63,"stroke":64},"454","272","54",[50,1478,1481],{"x":1479,"y":1480,"style":925},"590","78","known: read with the right engine",[43,1483],{"x":1474,"y":1484,"width":1475,"height":1476,"rx":62,"fill":186,"stroke":64},"112",[50,1486,1489],{"x":1479,"y":1487,"style":1488},"144","font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","unknown: \"this is not a workbook\"",[50,1491,1493],{"x":52,"y":1492,"style":88},"194","The uploader learns what went wrong; the job keeps running",[10,1495,1496,1497,1500,1501,1504],{},"Returning ",[13,1498,1499],{},"\"That file looks like an HTML export — please save it as .xlsx\""," is worth far more to the person uploading than a ",[13,1502,1503],{},"ValueError"," from deep inside pandas, and it removes an entire category of support requests.",[197,1506,1508],{"id":1507},"keep-the-engine-choice-in-one-place","Keep the engine choice in one place",[10,1510,1511,1512,1515],{},"Scattering ",[13,1513,1514],{},"engine="," across a codebase works until the day a format changes. Centralise the decision in a single module-level mapping, so switching every read from openpyxl to calamine — for speed, or because a format was added — is a one-line change rather than a search across the repository:",[205,1517,1519],{"className":296,"code":1518,"language":298,"meta":210,"style":210},"\"\"\"One place that knows which engine reads what.\"\"\"\nENGINE_BY_SUFFIX = {\n    \".xlsx\": \"openpyxl\", \".xlsm\": \"openpyxl\",\n    \".xls\": \"xlrd\", \".xlsb\": \"pyxlsb\", \".ods\": \"odf\",\n}\n\ndef engine_for(path) -> str:\n    from pathlib import Path\n    suffix = Path(path).suffix.lower()\n    try:\n        return ENGINE_BY_SUFFIX[suffix]\n    except KeyError:\n        raise ValueError(f\"no engine configured for {suffix!r} ({path})\") from None\n",[13,1520,1521,1526,1535,1556,1587,1591,1595,1609,1620,1630,1637,1647,1657],{"__ignoreMap":210},[214,1522,1523],{"class":102,"line":216},[214,1524,1525],{"class":223},"\"\"\"One place that knows which engine reads what.\"\"\"\n",[214,1527,1528,1531,1533],{"class":102,"line":237},[214,1529,1530],{"class":430},"ENGINE_BY_SUFFIX",[214,1532,986],{"class":305},[214,1534,1157],{"class":309},[214,1536,1537,1540,1542,1544,1546,1549,1551,1553],{"class":102,"line":250},[214,1538,1539],{"class":223},"    \".xlsx\"",[214,1541,994],{"class":309},[214,1543,348],{"class":223},[214,1545,339],{"class":309},[214,1547,1548],{"class":223},"\".xlsm\"",[214,1550,994],{"class":309},[214,1552,348],{"class":223},[214,1554,1555],{"class":309},",\n",[214,1557,1558,1561,1563,1565,1567,1570,1572,1575,1577,1580,1582,1585],{"class":102,"line":263},[214,1559,1560],{"class":223},"    \".xls\"",[214,1562,994],{"class":309},[214,1564,536],{"class":223},[214,1566,339],{"class":309},[214,1568,1569],{"class":223},"\".xlsb\"",[214,1571,994],{"class":309},[214,1573,1574],{"class":223},"\"pyxlsb\"",[214,1576,339],{"class":309},[214,1578,1579],{"class":223},"\".ods\"",[214,1581,994],{"class":309},[214,1583,1584],{"class":223},"\"odf\"",[214,1586,1555],{"class":309},[214,1588,1589],{"class":102,"line":276},[214,1590,1009],{"class":309},[214,1592,1593],{"class":102,"line":417},[214,1594,322],{"emptyLinePlaceholder":321},[214,1596,1597,1599,1602,1605,1607],{"class":102,"line":437},[214,1598,420],{"class":305},[214,1600,1601],{"class":423}," engine_for",[214,1603,1604],{"class":309},"(path) -> ",[214,1606,431],{"class":430},[214,1608,478],{"class":309},[214,1610,1611,1614,1616,1618],{"class":102,"line":454},[214,1612,1613],{"class":305},"    from",[214,1615,391],{"class":309},[214,1617,306],{"class":305},[214,1619,396],{"class":309},[214,1621,1622,1625,1627],{"class":102,"line":481},[214,1623,1624],{"class":309},"    suffix ",[214,1626,330],{"class":305},[214,1628,1629],{"class":309}," Path(path).suffix.lower()\n",[214,1631,1632,1635],{"class":102,"line":498},[214,1633,1634],{"class":305},"    try",[214,1636,478],{"class":309},[214,1638,1639,1641,1644],{"class":102,"line":525},[214,1640,484],{"class":305},[214,1642,1643],{"class":430}," ENGINE_BY_SUFFIX",[214,1645,1646],{"class":309},"[suffix]\n",[214,1648,1649,1652,1655],{"class":102,"line":541},[214,1650,1651],{"class":305},"    except",[214,1653,1654],{"class":430}," KeyError",[214,1656,478],{"class":309},[214,1658,1659,1662,1664,1666,1668,1671,1673,1676,1678,1680,1682,1684,1687,1689,1692,1695,1697],{"class":102,"line":576},[214,1660,1661],{"class":305},"        raise",[214,1663,804],{"class":430},[214,1665,807],{"class":309},[214,1667,810],{"class":305},[214,1669,1670],{"class":223},"\"no engine configured for ",[214,1672,816],{"class":517},[214,1674,1675],{"class":309},"suffix",[214,1677,822],{"class":305},[214,1679,825],{"class":517},[214,1681,557],{"class":223},[214,1683,816],{"class":517},[214,1685,1686],{"class":309},"path",[214,1688,825],{"class":517},[214,1690,1691],{"class":223},")\"",[214,1693,1694],{"class":309},") ",[214,1696,388],{"class":305},[214,1698,1699],{"class":430}," None\n",[10,1701,1702,1703,22],{},"That also gives you a place to hang a feature flag: reading the mapping from configuration lets an operator switch engines in a running deployment without a release, which is useful when a new file format shows up unannounced. The configuration pattern itself is covered in ",[18,1704,1706],{"href":1705},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Fkeep-excel-report-settings-in-a-config-file\u002F","Keep Excel report settings in a config file",[197,1708,1710],{"id":1709},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1712,1713,1714,1729,1746,1759,1775],"ul",{},[1715,1716,1717,1728],"li",{},[1718,1719,1720,1721,1724,1725,22],"strong",{},"Reading a ",[13,1722,1723],{},".csv"," with ",[13,1726,1727],{},"read_excel"," No engine can help; a CSV is not a workbook. Route on content, as above.",[1715,1730,1731,1734,1735,1738,1739,1741,1742,1745],{},[1718,1732,1733],{},"Passing a file object opened in text mode."," ",[13,1736,1737],{},"open(path)"," yields text; ",[13,1740,1727],{}," needs binary. Use ",[13,1743,1744],{},"open(path, \"rb\")"," or pass the path itself.",[1715,1747,1748,1751,1752,1754,1755,1758],{},[1718,1749,1750],{},"A consumed buffer."," Reading a ",[13,1753,669],{}," twice returns nothing the second time unless you ",[13,1756,1757],{},"seek(0)"," first — a common cause of a confusing empty-DataFrame result right after this error is fixed.",[1715,1760,1761,1767,1768,1770,1771,22],{},[1718,1762,1763,1766],{},[13,1764,1765],{},"engine=\"xlrd\""," on a modern file."," xlrd 2.0 handles ",[13,1769,372],{}," only, and this is one of the errors it produces — see ",[18,1772,1774],{"href":1773},"\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 format\"",[1715,1776,1777,1780],{},[1718,1778,1779],{},"Assuming the extension is authoritative"," in a folder of user uploads. It never is; validate every upload before parsing.",[197,1782,1784],{"id":1783},"performance-and-scale-notes","Performance and scale notes",[10,1786,1787,1788,1792,1793,22],{},"Explicit engines are marginally faster than detection because pandas skips the sniffing step, but the real gain is predictability: a job that names its engine cannot silently switch readers when a file arrives with a different extension. For large files the engine choice dominates everything else — calamine typically reads several times faster than openpyxl and uses less memory, at the cost of ignoring formatting. Benchmarks and the trade-off are in ",[18,1789,1791],{"href":1790},"\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",", and chunked strategies for very large workbooks are in ",[18,1794,1796],{"href":1795},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas\u002F","Read a large Excel file in chunks with pandas",[197,1798,1800],{"id":1799},"conclusion","Conclusion",[10,1802,1803,1804,1806],{},"pandas raises this error when it cannot identify the format from a filename or from the leading bytes — most often because the file has no extension, has the wrong one, or arrived as an unnamed buffer. Pass ",[13,1805,1514],{}," when you know the format, and dispatch on magic bytes when you do not. Add a start-up check that the engines you depend on are installed, and the error stops appearing in unattended runs altogether.",[197,1808,1810],{"id":1809},"frequently-asked-questions","Frequently asked questions",[10,1812,1813,1816,1817,1819,1820,1822],{},[1718,1814,1815],{},"What exactly does pandas look at to choose an engine?","\nThe filename extension when it has one, and otherwise the first bytes of the stream. If neither identifies a supported format — because the object is a bare ",[13,1818,669],{},", or the extension is wrong — it gives up with this ",[13,1821,1503],{}," rather than guessing.",[10,1824,1825,1828,1829,1831,1832,1834,1835,1837],{},[1718,1826,1827],{},"Why does it happen only for downloaded files?","\nA response body handed to ",[13,1830,1727],{}," as ",[13,1833,669],{}," carries no filename, so the extension route is unavailable. Pass ",[13,1836,1514],{}," explicitly, or give the buffer a name by writing it to a temporary file first.",[10,1839,1840,1843,1844,1847],{},[1718,1841,1842],{},"Does installing openpyxl fix it?","\nOnly when the message names a missing dependency. \"Format cannot be determined\" is about identification, not installation — a missing engine produces a different ",[13,1845,1846],{},"ImportError"," naming the package to install.",[10,1849,1850,1853,1854,1856],{},[1718,1851,1852],{},"Can I make pandas ignore the extension?","\nYes. The ",[13,1855,1514],{}," argument overrides detection completely, which is why naming it is the durable fix for files whose extension is wrong or absent.",[10,1858,1859,1862,1863,1866],{},[1718,1860,1861],{},"What if the file turns out to be a CSV?","\nRead it with ",[13,1864,1865],{},"read_csv"," instead. A CSV has no sheets, so no Excel engine can open it — the extension is simply a lie told by whatever exported it.",[197,1868,1870],{"id":1869},"related","Related",[1712,1872,1873,1881,1888,1895,1902],{},[1715,1874,1875,1734,1878,1880],{},[1718,1876,1877],{},"Up:",[18,1879,21],{"href":20}," — the full triage map, including the errors raised above and below this one.",[1715,1882,1883,1887],{},[18,1884,1886],{"href":1885},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-badzipfile-error-when-reading-excel-in-python\u002F","Fix BadZipFile when reading an Excel file in Python"," — what you get instead when detection succeeds but the container is broken.",[1715,1889,1890,1894],{},[18,1891,1893],{"href":1892},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-worksheet-does-not-exist-keyerror-in-openpyxl\u002F","Fix \"Worksheet does not exist\" KeyError in openpyxl"," — the next failure along, once the file opens.",[1715,1896,1897,1901],{},[18,1898,1900],{"href":1899},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhow-to-read-excel-with-pandas-step-by-step\u002F","How to read Excel with pandas step by step"," — the read path this error interrupts, from the beginning.",[1715,1903,1904,1906],{},[18,1905,873],{"href":872}," — downloading and reading without a filename anywhere in the process.",[1908,1909,1910],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":210,"searchDepth":237,"depth":237,"links":1912},[1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924],{"id":199,"depth":237,"text":200},{"id":289,"depth":237,"text":290},{"id":361,"depth":237,"text":362},{"id":662,"depth":237,"text":663},{"id":949,"depth":237,"text":950},{"id":1133,"depth":237,"text":1134},{"id":1507,"depth":237,"text":1508},{"id":1709,"depth":237,"text":1710},{"id":1783,"depth":237,"text":1784},{"id":1799,"depth":237,"text":1800},{"id":1809,"depth":237,"text":1810},{"id":1869,"depth":237,"text":1870},"2026-08-27","Why pandas cannot pick an engine for your spreadsheet, how to name the engine explicitly, and how to handle streams, URLs and files with no extension at all.","md",[1929,1931,1933,1935,1937],{"q":1815,"a":1930},"The filename extension when it has one, and otherwise the first bytes of the stream. If neither identifies a supported format — because the object is a bare BytesIO, or the extension is wrong — it gives up with this ValueError rather than guessing.",{"q":1827,"a":1932},"A response body handed to read_excel as BytesIO carries no filename, so the extension route is unavailable. Pass engine= explicitly, or give the buffer a name by writing it to a temporary file first.",{"q":1842,"a":1934},"Only when the message names a missing dependency. \"Format cannot be determined\" is about identification, not installation — a missing engine produces a different ImportError naming the package to install.",{"q":1852,"a":1936},"Yes. The engine= argument overrides detection completely, which is why naming it is the durable fix for files whose extension is wrong or absent.",{"q":1861,"a":1938},"Read it with read_csv instead. A CSV has no sheets, so no Excel engine can open it — the extension is simply a lie told by whatever exported it.",{},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-cannot-be-determined-in-pandas",{"title":1942,"description":1943},"Fix Excel File Format Cannot Be Determined","Solve pandas' ValueError on read_excel: choose the engine yourself, handle BytesIO and URLs with no filename, install the missing reader, and detect the real format.","fix-excel-file-format-cannot-be-determined-in-pandas","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-file-format-cannot-be-determined-in-pandas\u002Findex","how-to","FVeW5oC2-pojPOdGvy4958BnX2cmJMO89Kq4-qLkXhY",[1949,1953],{"title":1950,"path":1951,"stem":1952,"children":-1},"Fix BadZipFile When Reading an Excel File in Python","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-badzipfile-error-when-reading-excel-in-python","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-badzipfile-error-when-reading-excel-in-python\u002Findex",{"title":1954,"path":1955,"stem":1956,"children":-1},"Fix \"Excel Found Unreadable Content\" After Writing with Python","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-repaired-content-after-writing-with-python","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-repaired-content-after-writing-with-python\u002Findex",1788710155006]