[{"data":1,"prerenderedAt":2042},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl":2006,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl":2015,"children:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl":2020,"related:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl":2021},{"id":4,"title":5,"body":6,"dateModified":1972,"datePublished":1972,"description":1973,"extension":1974,"faq":1975,"keyword":1984,"kind":1985,"meta":1986,"navigation":280,"path":1995,"seo":1996,"slug":1999,"stem":2000,"topics":2001,"type":2004,"__hash__":2005},"docs\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl\u002Findex.md","Fix NoneType Errors When Reading Cells with openpyxl",{"type":7,"value":8,"toc":1958},"minimark",[9,13,24,44,49,72,82,242,246,252,422,428,593,597,600,852,863,867,884,981,984,1038,1101,1118,1193,1197,1221,1342,1350,1354,1369,1459,1466,1470,1495,1613,1620,1624,1627,1816,1820,1875,1879,1890,1894,1900,1906,1912,1918,1922,1954],[10,11,12],"p",{},"A script that reads cells with openpyxl runs fine on one workbook and fails on the next with one of these:",[14,15,21],"pre",{"className":16,"code":18,"language":19,"meta":20},[17],"language-text","TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'\nAttributeError: 'NoneType' object has no attribute 'strip'\nTypeError: '\u003C' not supported between instances of 'NoneType' and 'float'\n","text","",[22,23,18],"code",{"__ignoreMap":20},[10,25,26,27,30,31,34,35,37,38,43],{},"All three mean the same thing: a cell's ",[22,28,29],{},".value"," was ",[22,32,33],{},"None",", and the code assumed it would be a number or a string. openpyxl returns ",[22,36,33],{}," for four different reasons, and the right fix depends on which one it is. This page shows how to find the cell, tells the four cases apart and fixes each. It is part of ",[39,40,42],"a",{"href":41},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002F","Troubleshooting Common Python Excel Errors",".",[45,46,48],"h2",{"id":47},"prerequisites","Prerequisites",[14,50,54],{"className":51,"code":52,"language":53,"meta":20,"style":20},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash",[22,55,56],{"__ignoreMap":20},[57,58,61,65,69],"span",{"class":59,"line":60},"line",1,[57,62,64],{"class":63},"sMTad","pip",[57,66,68],{"class":67},"srMev"," install",[57,70,71],{"class":67}," openpyxl\n",[10,73,74,77,78,81],{},[22,75,76],{},"sales.xlsx"," is a small sheet of quarterly sales per rep, written by a Python script: a header row, five data rows, a ",[22,79,80],{},"Total"," column of formulas, a merged region cell for North, one rep missing a Q2 figure, one row with no rep name, and some shaded but empty rows below the data — the kind of sheet people hand over.",[83,84,92,93,92,97,92,101,92,109,92,119,92,130,92,136,92,142,92,147,92,151,92,156,92,163,92,168,92,172,92,177,92,181,92,185,92,190,92,197,92,202,92,206,92,209,92,212,92,217,92,224,92,229,92,232,92,236,92,239],"svg",{"viewBox":85,"role":86,"ariaLabelledBy":87,"xmlns":90,"style":91},"4 0 752 200","img",[88,89],"nt-causes-t","nt-causes-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  ",[94,95,96],"title",{"id":88},"Four reasons a cell value is None",[98,99,100],"desc",{"id":89},"Four panels. An empty cell has no value. A formula cell read with data_only returns None if the file was never calculated by a spreadsheet application. A merged range stores its value only in the top-left cell, so the others are None. Formatted but empty rows below the data extend max_row and yield None rows.",[102,103],"rect",{"x":104,"y":105,"width":106,"height":107,"fill":108},"4","0","752","200","#ffffff",[102,110],{"x":111,"y":112,"width":113,"height":114,"rx":115,"fill":116,"stroke":117,"style":118},"20.0","16.0","166.5","168.0","14","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[120,121,123],"g",{"transform":122},"translate(90.2,29.0) scale(1.083)",[124,125],"path",{"d":126,"fill":127,"stroke":128,"style":129},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","none","var(--text,#172033)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[19,131,135],{"x":132,"y":133,"style":134},"103.2","78.0","font-size:14px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Empty cell",[59,137],{"x1":138,"y1":139,"x2":140,"y2":139,"stroke":117,"style":141},"36.0","91.0","170.5","stroke-width:1px",[19,143,146],{"x":132,"y":144,"style":145},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","genuinely blank",[19,148,150],{"x":132,"y":149,"style":145},"137.0","decide: skip or default",[102,152],{"x":153,"y":112,"width":113,"height":114,"rx":115,"fill":154,"stroke":155,"style":118},"204.5","#fce9e9","var(--danger,#dc2626)",[120,157,159],{"transform":158},"translate(274.8,29.0) scale(1.083)",[124,160],{"d":161,"fill":127,"stroke":162,"style":129},"M9 8l-4 4 4 4 M15 8l4 4-4 4","var(--accent-ink,#be185d)",[19,164,167],{"x":165,"y":133,"style":166},"287.8","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Uncalculated",[19,169,171],{"x":165,"y":170,"style":166},"97.0","formula",[59,173],{"x1":174,"y1":175,"x2":176,"y2":175,"stroke":155,"style":141},"220.5","110.0","355.0",[19,178,180],{"x":165,"y":179,"style":145},"130.0","data_only=True",[19,182,184],{"x":165,"y":183,"style":145},"156.0","no cached result",[102,186],{"x":187,"y":112,"width":113,"height":114,"rx":115,"fill":188,"stroke":189,"style":118},"389.0","#fdefd8","var(--gold,#b4740a)",[120,191,193],{"transform":192},"translate(459.2,29.0) scale(1.083)",[124,194],{"d":195,"fill":127,"stroke":196,"style":129},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--gold-ink,#7a4e06)",[19,198,201],{"x":199,"y":133,"style":200},"472.2","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Merged range",[59,203],{"x1":204,"y1":139,"x2":205,"y2":139,"stroke":189,"style":141},"405.0","539.5",[19,207,208],{"x":199,"y":144,"style":145},"value in top-left only",[19,210,211],{"x":199,"y":149,"style":145},"others read None",[102,213],{"x":214,"y":112,"width":113,"height":114,"rx":115,"fill":215,"stroke":216,"style":118},"573.5","#d9f4f1","var(--teal,#0f9488)",[120,218,220],{"transform":219},"translate(643.8,29.0) scale(1.083)",[124,221],{"d":222,"fill":127,"stroke":223,"style":129},"M3 5h18l-7 8v6l-4 2v-8z","var(--teal-ink,#0b6157)",[19,225,228],{"x":226,"y":133,"style":227},"656.8","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Formatted empty",[19,230,231],{"x":226,"y":170,"style":227},"rows",[59,233],{"x1":234,"y1":175,"x2":235,"y2":175,"stroke":216,"style":141},"589.5","724.0",[19,237,238],{"x":226,"y":179,"style":145},"max_row too far",[19,240,241],{"x":226,"y":183,"style":145},"whole rows of None",[45,243,245],{"id":244},"step-1-find-the-cell-that-is-none","Step 1 — Find the cell that is None",[10,247,248,249,251],{},"The traceback names the line, not the cell. Before changing anything, print which cells are ",[22,250,33],{}," in the range the code reads; the pattern usually identifies the cause at once:",[14,253,257],{"className":254,"code":255,"language":256,"meta":20,"style":20},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\n\nws = load_workbook(\"sales.xlsx\")[\"Sales\"]\nprint(\"max_row:\", ws.max_row)\nfor row in ws.iter_rows(min_row=2):\n    empty = [c.coordinate for c in row if c.value is None]\n    if empty:\n        print(row[0].row, empty, type(row[0]).__name__)\n","python",[22,258,259,275,282,306,322,349,383,392],{"__ignoreMap":20},[57,260,261,265,269,272],{"class":59,"line":60},[57,262,264],{"class":263},"s-kum","from",[57,266,268],{"class":267},"skGVy"," openpyxl ",[57,270,271],{"class":263},"import",[57,273,274],{"class":267}," load_workbook\n",[57,276,278],{"class":59,"line":277},2,[57,279,281],{"emptyLinePlaceholder":280},true,"\n",[57,283,285,288,291,294,297,300,303],{"class":59,"line":284},3,[57,286,287],{"class":267},"ws ",[57,289,290],{"class":263},"=",[57,292,293],{"class":267}," load_workbook(",[57,295,296],{"class":67},"\"sales.xlsx\"",[57,298,299],{"class":267},")[",[57,301,302],{"class":67},"\"Sales\"",[57,304,305],{"class":267},"]\n",[57,307,309,313,316,319],{"class":59,"line":308},4,[57,310,312],{"class":311},"sP0c6","print",[57,314,315],{"class":267},"(",[57,317,318],{"class":67},"\"max_row:\"",[57,320,321],{"class":267},", ws.max_row)\n",[57,323,325,328,331,334,337,341,343,346],{"class":59,"line":324},5,[57,326,327],{"class":263},"for",[57,329,330],{"class":267}," row ",[57,332,333],{"class":263},"in",[57,335,336],{"class":267}," ws.iter_rows(",[57,338,340],{"class":339},"sa561","min_row",[57,342,290],{"class":263},[57,344,345],{"class":311},"2",[57,347,348],{"class":267},"):\n",[57,350,352,355,357,360,362,365,367,369,372,375,378,381],{"class":59,"line":351},6,[57,353,354],{"class":267},"    empty ",[57,356,290],{"class":263},[57,358,359],{"class":267}," [c.coordinate ",[57,361,327],{"class":263},[57,363,364],{"class":267}," c ",[57,366,333],{"class":263},[57,368,330],{"class":267},[57,370,371],{"class":263},"if",[57,373,374],{"class":267}," c.value ",[57,376,377],{"class":263},"is",[57,379,380],{"class":311}," None",[57,382,305],{"class":267},[57,384,386,389],{"class":59,"line":385},7,[57,387,388],{"class":263},"    if",[57,390,391],{"class":267}," empty:\n",[57,393,395,398,401,403,406,409,411,413,416,419],{"class":59,"line":394},8,[57,396,397],{"class":311},"        print",[57,399,400],{"class":267},"(row[",[57,402,105],{"class":311},[57,404,405],{"class":267},"].row, empty, ",[57,407,408],{"class":311},"type",[57,410,400],{"class":267},[57,412,105],{"class":311},[57,414,415],{"class":267},"]).",[57,417,418],{"class":311},"__name__",[57,420,421],{"class":267},")\n",[10,423,424,425,427],{},"The pattern is the diagnosis. Four different pictures appear here: row 3 has a ",[22,426,33],{}," in column A (a merged cell) and D (a missing figure); row 4 has no rep in B; and rows 7 to 20 are entirely empty yet counted, because rows 8 to 20 carry a fill and that extends the sheet's used range.",[83,429,92,434,92,437,92,440,92,443,92,451,92,459,92,464,92,468,92,472,92,475,92,480,92,488,92,493,92,497,92,502,92,506,92,511,92,514,92,517,92,521,92,524,92,527,92,532,92,535,92,538,92,541,92,544,92,548,92,551,92,554,92,559,92,563,92,566,92,568,92,571,92,575,92,578,92,581,92,586,92,590],{"viewBox":430,"role":86,"ariaLabelledBy":431,"xmlns":90,"style":91},"4 0 752 272",[432,433],"nt-which-t","nt-which-d",[94,435,436],{"id":432},"Reading the pattern of None cells",[98,438,439],{"id":433},"A decision diagram. If only formula cells are None, the file has no cached results. If the None cell is a MergedCell, read the range's top-left cell. If whole rows at the end are None, formatting extends max_row. If scattered single cells are None, they are genuinely blank.",[102,441],{"x":104,"y":105,"width":106,"height":442,"fill":108},"272",[102,444],{"x":445,"y":112,"width":446,"height":447,"rx":448,"fill":449,"stroke":450,"style":118},"240.0","280.0","75.5","12","#f0f4ff","var(--brand,#5b5cf0)",[120,452,454],{"transform":453},"translate(368.0,29.0) scale(1.000)",[124,455],{"d":456,"fill":127,"stroke":457,"style":458},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[19,460,463],{"x":461,"y":447,"style":462},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Which cells are None?",[59,465],{"x1":461,"y1":466,"x2":461,"y2":467,"stroke":450,"style":118},"91.5","111.5",[59,469],{"x1":470,"y1":467,"x2":471,"y2":467,"stroke":450,"style":118},"104.0","656.0",[59,473],{"x1":470,"y1":467,"x2":470,"y2":474,"stroke":450,"style":118},"157.5",[476,477],"polygon",{"points":478,"fill":479},"104.0,161.5 99.0,152.5 109.0,152.5","#5b5cf0",[102,481],{"x":482,"y":483,"width":484,"height":485,"rx":486,"fill":108,"stroke":117,"style":487},"42.6","121.5","122.8","21.0","10","stroke-width:1.2px",[19,489,492],{"x":470,"y":490,"style":491},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","only formula cells",[102,494],{"x":111,"y":495,"width":114,"height":496,"rx":448,"fill":154,"stroke":155,"style":118},"163.5","93.0",[120,498,500],{"transform":499},"translate(92.0,176.5) scale(1.000)",[124,501],{"d":161,"fill":127,"stroke":162,"style":458},[19,503,167],{"x":470,"y":504,"style":505},"223.0","font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[19,507,510],{"x":470,"y":508,"style":509},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Step 3",[59,512],{"x1":513,"y1":467,"x2":513,"y2":474,"stroke":450,"style":118},"288.0",[476,515],{"points":516,"fill":479},"288.0,161.5 283.0,152.5 293.0,152.5",[102,518],{"x":519,"y":483,"width":520,"height":485,"rx":486,"fill":108,"stroke":117,"style":487},"230.1","115.8",[19,522,523],{"x":513,"y":490,"style":491},"MergedCell type",[102,525],{"x":526,"y":495,"width":114,"height":496,"rx":448,"fill":188,"stroke":189,"style":118},"204.0",[120,528,530],{"transform":529},"translate(276.0,176.5) scale(1.000)",[124,531],{"d":195,"fill":127,"stroke":196,"style":458},[19,533,201],{"x":513,"y":504,"style":534},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[19,536,537],{"x":513,"y":508,"style":509},"Step 4",[59,539],{"x1":540,"y1":467,"x2":540,"y2":474,"stroke":450,"style":118},"472.0",[476,542],{"points":543,"fill":479},"472.0,161.5 467.0,152.5 477.0,152.5",[102,545],{"x":546,"y":483,"width":547,"height":485,"rx":486,"fill":108,"stroke":117,"style":487},"398.3","147.3",[19,549,550],{"x":540,"y":490,"style":491},"whole rows at the end",[102,552],{"x":553,"y":495,"width":114,"height":496,"rx":448,"fill":215,"stroke":216,"style":118},"388.0",[120,555,557],{"transform":556},"translate(460.0,176.5) scale(1.000)",[124,558],{"d":222,"fill":127,"stroke":223,"style":458},[19,560,562],{"x":540,"y":504,"style":561},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Formatted rows",[19,564,565],{"x":540,"y":508,"style":509},"Step 5",[59,567],{"x1":471,"y1":467,"x2":471,"y2":474,"stroke":450,"style":118},[476,569],{"points":570,"fill":479},"656.0,161.5 651.0,152.5 661.0,152.5",[102,572],{"x":573,"y":483,"width":574,"height":485,"rx":486,"fill":108,"stroke":117,"style":487},"603.5","105.1",[19,576,577],{"x":471,"y":490,"style":491},"scattered cells",[102,579],{"x":580,"y":495,"width":114,"height":496,"rx":448,"fill":116,"stroke":117,"style":118},"572.0",[120,582,584],{"transform":583},"translate(644.0,176.5) scale(1.000)",[124,585],{"d":126,"fill":127,"stroke":128,"style":458},[19,587,589],{"x":471,"y":504,"style":588},"font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Blank",[19,591,592],{"x":471,"y":508,"style":509},"Step 2",[45,594,596],{"id":595},"step-2-empty-cells-decide-what-they-mean","Step 2 — Empty cells: decide what they mean",[10,598,599],{},"A blank Q2 figure could mean zero, \"not reported yet\" or \"not applicable\". The code should say which, in one place, rather than crashing or silently treating blanks as zero:",[14,601,603],{"className":254,"code":602,"language":256,"meta":20,"style":20},"def number(value, default=None):\n    \"\"\"Return value as a float, or default for blank cells; raise for text that is not a number.\"\"\"\n    if value is None or (isinstance(value, str) and not value.strip()):\n        return default\n    return float(value)\n\ndef text(value, default=\"\"):\n    return default if value is None else str(value).strip()\n\nfor row in ws.iter_rows(min_row=2, max_row=6, values_only=True):\n    region, rep, q1, q2, _ = row\n    print(f\"{text(rep, '(no rep)'):9} Q1={number(q1)}  Q2={number(q2, default=0.0)}\")\n",[22,604,605,623,628,666,674,685,689,705,729,734,774,785],{"__ignoreMap":20},[57,606,607,610,614,617,619,621],{"class":59,"line":60},[57,608,609],{"class":263},"def",[57,611,613],{"class":612},"s_Opv"," number",[57,615,616],{"class":267},"(value, default",[57,618,290],{"class":263},[57,620,33],{"class":311},[57,622,348],{"class":267},[57,624,625],{"class":59,"line":277},[57,626,627],{"class":67},"    \"\"\"Return value as a float, or default for blank cells; raise for text that is not a number.\"\"\"\n",[57,629,630,632,635,637,639,642,645,648,651,654,657,660,663],{"class":59,"line":284},[57,631,388],{"class":263},[57,633,634],{"class":267}," value ",[57,636,377],{"class":263},[57,638,380],{"class":311},[57,640,641],{"class":263}," or",[57,643,644],{"class":267}," (",[57,646,647],{"class":311},"isinstance",[57,649,650],{"class":267},"(value, ",[57,652,653],{"class":311},"str",[57,655,656],{"class":267},") ",[57,658,659],{"class":263},"and",[57,661,662],{"class":263}," not",[57,664,665],{"class":267}," value.strip()):\n",[57,667,668,671],{"class":59,"line":308},[57,669,670],{"class":263},"        return",[57,672,673],{"class":267}," default\n",[57,675,676,679,682],{"class":59,"line":324},[57,677,678],{"class":263},"    return",[57,680,681],{"class":311}," float",[57,683,684],{"class":267},"(value)\n",[57,686,687],{"class":59,"line":351},[57,688,281],{"emptyLinePlaceholder":280},[57,690,691,693,696,698,700,703],{"class":59,"line":385},[57,692,609],{"class":263},[57,694,695],{"class":612}," text",[57,697,616],{"class":267},[57,699,290],{"class":263},[57,701,702],{"class":67},"\"\"",[57,704,348],{"class":267},[57,706,707,709,712,714,716,718,720,723,726],{"class":59,"line":394},[57,708,678],{"class":263},[57,710,711],{"class":267}," default ",[57,713,371],{"class":263},[57,715,634],{"class":267},[57,717,377],{"class":263},[57,719,380],{"class":311},[57,721,722],{"class":263}," else",[57,724,725],{"class":311}," str",[57,727,728],{"class":267},"(value).strip()\n",[57,730,732],{"class":59,"line":731},9,[57,733,281],{"emptyLinePlaceholder":280},[57,735,737,739,741,743,745,747,749,751,754,757,759,762,764,767,769,772],{"class":59,"line":736},10,[57,738,327],{"class":263},[57,740,330],{"class":267},[57,742,333],{"class":263},[57,744,336],{"class":267},[57,746,340],{"class":339},[57,748,290],{"class":263},[57,750,345],{"class":311},[57,752,753],{"class":267},", ",[57,755,756],{"class":339},"max_row",[57,758,290],{"class":263},[57,760,761],{"class":311},"6",[57,763,753],{"class":267},[57,765,766],{"class":339},"values_only",[57,768,290],{"class":263},[57,770,771],{"class":311},"True",[57,773,348],{"class":267},[57,775,777,780,782],{"class":59,"line":776},11,[57,778,779],{"class":267},"    region, rep, q1, q2, _ ",[57,781,290],{"class":263},[57,783,784],{"class":267}," row\n",[57,786,788,791,793,796,799,803,806,809,812,815,818,821,823,826,828,831,833,836,839,841,844,846,848,850],{"class":59,"line":787},12,[57,789,790],{"class":311},"    print",[57,792,315],{"class":267},[57,794,795],{"class":263},"f",[57,797,798],{"class":67},"\"",[57,800,802],{"class":801},"sSjpA","{",[57,804,805],{"class":267},"text(rep, ",[57,807,808],{"class":67},"'(no rep)'",[57,810,811],{"class":267},")",[57,813,814],{"class":263},":9",[57,816,817],{"class":801},"}",[57,819,820],{"class":67}," Q1=",[57,822,802],{"class":801},[57,824,825],{"class":267},"number(q1)",[57,827,817],{"class":801},[57,829,830],{"class":67},"  Q2=",[57,832,802],{"class":801},[57,834,835],{"class":267},"number(q2, ",[57,837,838],{"class":339},"default",[57,840,290],{"class":263},[57,842,843],{"class":311},"0.0",[57,845,811],{"class":267},[57,847,817],{"class":801},[57,849,798],{"class":67},[57,851,421],{"class":267},[10,853,854,855,858,859,862],{},"Treating missing Q2 as zero is a business decision; for an average it would be wrong, since a zero pulls the mean down. Keep the default explicit at each call site, so a reviewer can see what each blank turns into. Converting text with ",[22,856,857],{},"float()"," still raises for a cell like ",[22,860,861],{},"\"n\u002Fa\"",", which is what you want: a surprising value should stop the script rather than become zero.",[45,864,866],{"id":865},"step-3-formulas-that-read-as-none","Step 3 — Formulas that read as None",[10,868,869,870,872,873,875,876,880,881,883],{},"The ",[22,871,80],{}," column holds formulas. Loaded normally, openpyxl returns the formula text; loaded with ",[22,874,180],{},", it returns the ",[877,878,879],"em",{},"cached result",", the value the spreadsheet application stored when it last calculated the file. A file written by openpyxl or xlsxwriter has never been calculated, so there is no cached result, and every formula cell reads as ",[22,882,33],{},":",[14,885,887],{"className":254,"code":886,"language":256,"meta":20,"style":20},"print(\"formulas:\", [c.value for c in load_workbook(\"sales.xlsx\")[\"Sales\"][\"E\"][1:4]])\nprint(\"data_only:\", [c.value for c in load_workbook(\"sales.xlsx\", data_only=True)[\"Sales\"][\"E\"][1:4]])\n",[22,888,889,933],{"__ignoreMap":20},[57,890,891,893,895,898,901,903,905,907,909,911,913,915,918,921,923,926,928,930],{"class":59,"line":60},[57,892,312],{"class":311},[57,894,315],{"class":267},[57,896,897],{"class":67},"\"formulas:\"",[57,899,900],{"class":267},", [c.value ",[57,902,327],{"class":263},[57,904,364],{"class":267},[57,906,333],{"class":263},[57,908,293],{"class":267},[57,910,296],{"class":67},[57,912,299],{"class":267},[57,914,302],{"class":67},[57,916,917],{"class":267},"][",[57,919,920],{"class":67},"\"E\"",[57,922,917],{"class":267},[57,924,925],{"class":311},"1",[57,927,883],{"class":267},[57,929,104],{"class":311},[57,931,932],{"class":267},"]])\n",[57,934,935,937,939,942,944,946,948,950,952,954,956,959,961,963,965,967,969,971,973,975,977,979],{"class":59,"line":277},[57,936,312],{"class":311},[57,938,315],{"class":267},[57,940,941],{"class":67},"\"data_only:\"",[57,943,900],{"class":267},[57,945,327],{"class":263},[57,947,364],{"class":267},[57,949,333],{"class":263},[57,951,293],{"class":267},[57,953,296],{"class":67},[57,955,753],{"class":267},[57,957,958],{"class":339},"data_only",[57,960,290],{"class":263},[57,962,771],{"class":311},[57,964,299],{"class":267},[57,966,302],{"class":67},[57,968,917],{"class":267},[57,970,920],{"class":67},[57,972,917],{"class":267},[57,974,925],{"class":311},[57,976,883],{"class":267},[57,978,104],{"class":311},[57,980,932],{"class":267},[10,982,983],{},"Open the file in Excel and save it, or recalculate it headlessly with LibreOffice, and the cached values appear:",[14,985,987],{"className":51,"code":986,"language":53,"meta":20,"style":20},"soffice -env:UserInstallation=file:\u002F\u002F$PWD\u002Flo_profile --headless --convert-to xlsx --outdir recalculated sales.xlsx > \u002Fdev\u002Fnull 2>&1\nls recalculated\n",[22,988,989,1030],{"__ignoreMap":20},[57,990,991,994,997,1000,1003,1006,1009,1012,1015,1018,1021,1024,1027],{"class":59,"line":60},[57,992,993],{"class":63},"soffice",[57,995,996],{"class":311}," -env:UserInstallation=file:\u002F\u002F",[57,998,999],{"class":267},"$PWD",[57,1001,1002],{"class":311},"\u002Flo_profile",[57,1004,1005],{"class":311}," --headless",[57,1007,1008],{"class":311}," --convert-to",[57,1010,1011],{"class":67}," xlsx",[57,1013,1014],{"class":311}," --outdir",[57,1016,1017],{"class":67}," recalculated",[57,1019,1020],{"class":67}," sales.xlsx",[57,1022,1023],{"class":263}," >",[57,1025,1026],{"class":67}," \u002Fdev\u002Fnull",[57,1028,1029],{"class":263}," 2>&1\n",[57,1031,1032,1035],{"class":59,"line":277},[57,1033,1034],{"class":63},"ls",[57,1036,1037],{"class":67}," recalculated\n",[14,1039,1041],{"className":254,"code":1040,"language":256,"meta":20,"style":20},"calc = load_workbook(\"recalculated\u002Fsales.xlsx\", data_only=True)[\"Sales\"]\nprint(\"after recalculation:\", [c.value for c in calc[\"E\"][1:6]])\n",[22,1042,1043,1069],{"__ignoreMap":20},[57,1044,1045,1048,1050,1052,1055,1057,1059,1061,1063,1065,1067],{"class":59,"line":60},[57,1046,1047],{"class":267},"calc ",[57,1049,290],{"class":263},[57,1051,293],{"class":267},[57,1053,1054],{"class":67},"\"recalculated\u002Fsales.xlsx\"",[57,1056,753],{"class":267},[57,1058,958],{"class":339},[57,1060,290],{"class":263},[57,1062,771],{"class":311},[57,1064,299],{"class":267},[57,1066,302],{"class":67},[57,1068,305],{"class":267},[57,1070,1071,1073,1075,1078,1080,1082,1084,1086,1089,1091,1093,1095,1097,1099],{"class":59,"line":277},[57,1072,312],{"class":311},[57,1074,315],{"class":267},[57,1076,1077],{"class":67},"\"after recalculation:\"",[57,1079,900],{"class":267},[57,1081,327],{"class":263},[57,1083,364],{"class":267},[57,1085,333],{"class":263},[57,1087,1088],{"class":267}," calc[",[57,1090,920],{"class":67},[57,1092,917],{"class":267},[57,1094,925],{"class":311},[57,1096,883],{"class":267},[57,1098,761],{"class":311},[57,1100,932],{"class":267},[10,1102,1103,1104,1107,1108,1112,1113,1117],{},"Row 3 now shows 980: LibreOffice treats the blank Q2 as zero in ",[22,1105,1106],{},"=C3+D3",". Often the simplest fix is not to read formula results at all: compute the total in Python from the inputs, which you have anyway. ",[39,1109,1111],{"href":1110},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fread-formula-results-with-openpyxl-data-only\u002F","Read Formula Results with openpyxl data_only"," and ",[39,1114,1116],{"href":1115},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel in Python"," cover both routes.",[83,1119,92,1125,92,1128,92,1131,92,1136,92,1142,92,1148,92,1152,92,1156,92,1159,92,1164,92,1166,92,1169,92,1172,92,1175,92,1177,92,1180,92,1183,92,1187,92,1189],{"viewBox":1120,"role":86,"ariaLabelledBy":1121,"xmlns":90,"style":1124},"239 0 282 144",[1122,1123],"nt-dataonly-t","nt-dataonly-d","width:100%;max-width:282px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[94,1126,1127],{"id":1122},"The same formula cells, loaded two ways",[98,1129,1130],{"id":1123},"A table comparing how cell E2 is read. Loaded normally, it returns the formula text. Loaded with data_only after being written by Python, it returns None. Loaded with data_only after a recalculation, it returns 2550.",[102,1132],{"x":1133,"y":105,"width":1134,"height":1135,"fill":108},"239","282","144",[102,1137],{"x":1138,"y":112,"width":1139,"height":1140,"rx":105,"fill":449,"stroke":450,"style":1141},"255.2","160.7","28.0","stroke-width:1.5px",[19,1143,1147],{"x":1144,"y":1145,"style":1146},"335.5","35.0","font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","How the file was loaded",[102,1149],{"x":1150,"y":112,"width":1151,"height":1140,"rx":105,"fill":449,"stroke":450,"style":1141},"415.8","89.0",[19,1153,1155],{"x":1154,"y":1145,"style":1146},"460.3","E2 reads as",[102,1157],{"x":1138,"y":1158,"width":1139,"height":1140,"rx":105,"fill":108,"stroke":117,"style":141},"44.0",[19,1160,1163],{"x":1144,"y":1161,"style":1162},"63.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","load_workbook(path)",[102,1165],{"x":1150,"y":1158,"width":1151,"height":1140,"rx":105,"fill":108,"stroke":117,"style":141},[19,1167,1168],{"x":1154,"y":1161,"style":1162},"=C2+D2",[102,1170],{"x":1138,"y":1171,"width":1139,"height":1140,"rx":105,"fill":108,"stroke":117,"style":141},"72.0",[19,1173,1174],{"x":1144,"y":139,"style":1162},"data_only (Python file)",[102,1176],{"x":1150,"y":1171,"width":1151,"height":1140,"rx":105,"fill":154,"stroke":155,"style":1141},[19,1178,33],{"x":1154,"y":139,"style":1179},"font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[102,1181],{"x":1138,"y":1182,"width":1139,"height":1140,"rx":105,"fill":108,"stroke":117,"style":141},"100.0",[19,1184,1186],{"x":1144,"y":1185,"style":1162},"119.0","data_only (recalculated)",[102,1188],{"x":1150,"y":1182,"width":1151,"height":1140,"rx":105,"fill":215,"stroke":216,"style":1141},[19,1190,1192],{"x":1154,"y":1185,"style":1191},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","2550",[45,1194,1196],{"id":1195},"step-4-merged-cells","Step 4 — Merged cells",[10,1198,1199,1202,1203,1206,1207,1210,1211,1214,1215,1217,1218,1220],{},[22,1200,1201],{},"A2:A3"," is merged. The value lives in ",[22,1204,1205],{},"A2","; ",[22,1208,1209],{},"A3"," is a ",[22,1212,1213],{},"MergedCell"," placeholder whose value is ",[22,1216,33],{},". Code that groups rows by region then puts row 3 in a ",[22,1219,33],{}," region. Fill the merged value into every cell of its range when reading:",[14,1222,1224],{"className":254,"code":1223,"language":256,"meta":20,"style":20},"def merged_value(ws, cell):\n    for rng in ws.merged_cells.ranges:\n        if cell.coordinate in rng:\n            return ws.cell(row=rng.min_row, column=rng.min_col).value\n    return cell.value\n\nprint([merged_value(ws, ws.cell(row=r, column=1)) for r in range(2, 7)])\n",[22,1225,1226,1236,1249,1262,1286,1293,1297],{"__ignoreMap":20},[57,1227,1228,1230,1233],{"class":59,"line":60},[57,1229,609],{"class":263},[57,1231,1232],{"class":612}," merged_value",[57,1234,1235],{"class":267},"(ws, cell):\n",[57,1237,1238,1241,1244,1246],{"class":59,"line":277},[57,1239,1240],{"class":263},"    for",[57,1242,1243],{"class":267}," rng ",[57,1245,333],{"class":263},[57,1247,1248],{"class":267}," ws.merged_cells.ranges:\n",[57,1250,1251,1254,1257,1259],{"class":59,"line":284},[57,1252,1253],{"class":263},"        if",[57,1255,1256],{"class":267}," cell.coordinate ",[57,1258,333],{"class":263},[57,1260,1261],{"class":267}," rng:\n",[57,1263,1264,1267,1270,1273,1275,1278,1281,1283],{"class":59,"line":308},[57,1265,1266],{"class":263},"            return",[57,1268,1269],{"class":267}," ws.cell(",[57,1271,1272],{"class":339},"row",[57,1274,290],{"class":263},[57,1276,1277],{"class":267},"rng.min_row, ",[57,1279,1280],{"class":339},"column",[57,1282,290],{"class":263},[57,1284,1285],{"class":267},"rng.min_col).value\n",[57,1287,1288,1290],{"class":59,"line":324},[57,1289,678],{"class":263},[57,1291,1292],{"class":267}," cell.value\n",[57,1294,1295],{"class":59,"line":351},[57,1296,281],{"emptyLinePlaceholder":280},[57,1298,1299,1301,1304,1306,1308,1311,1313,1315,1317,1320,1322,1325,1327,1330,1332,1334,1336,1339],{"class":59,"line":385},[57,1300,312],{"class":311},[57,1302,1303],{"class":267},"([merged_value(ws, ws.cell(",[57,1305,1272],{"class":339},[57,1307,290],{"class":263},[57,1309,1310],{"class":267},"r, ",[57,1312,1280],{"class":339},[57,1314,290],{"class":263},[57,1316,925],{"class":311},[57,1318,1319],{"class":267},")) ",[57,1321,327],{"class":263},[57,1323,1324],{"class":267}," r ",[57,1326,333],{"class":263},[57,1328,1329],{"class":311}," range",[57,1331,315],{"class":267},[57,1333,345],{"class":311},[57,1335,753],{"class":267},[57,1337,1338],{"class":311},"7",[57,1340,1341],{"class":267},")])\n",[10,1343,1344,1345,1349],{},"For a whole sheet, build a lookup of placeholder coordinates to their range's value once, rather than scanning the ranges per cell. ",[39,1346,1348],{"href":1347},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Funmerge-cells-and-fill-values-with-openpyxl\u002F","Unmerge Cells and Fill the Values with openpyxl"," does it for a whole sheet.",[45,1351,1353],{"id":1352},"step-5-formatted-empty-rows-at-the-end","Step 5 — Formatted empty rows at the end",[10,1355,1356,1358,1359,1362,1363,1365,1366,1368],{},[22,1357,756],{}," is the last row with ",[877,1360,1361],{},"anything"," in it, including formatting. Shaded rows 8 to 20 have no values, but loops that run to ",[22,1364,756],{}," see thirteen rows of ",[22,1367,33],{},". Stop at the last row with a value in a key column, or skip rows that are entirely empty:",[14,1370,1372],{"className":254,"code":1371,"language":256,"meta":20,"style":20},"data = [row for row in ws.iter_rows(min_row=2, values_only=True) if any(v is not None for v in row)]\nprint(len(data), \"data rows out of\", ws.max_row - 1)\n",[22,1373,1374,1433],{"__ignoreMap":20},[57,1375,1376,1379,1381,1384,1386,1388,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1410,1413,1416,1418,1420,1422,1425,1428,1430],{"class":59,"line":60},[57,1377,1378],{"class":267},"data ",[57,1380,290],{"class":263},[57,1382,1383],{"class":267}," [row ",[57,1385,327],{"class":263},[57,1387,330],{"class":267},[57,1389,333],{"class":263},[57,1391,336],{"class":267},[57,1393,340],{"class":339},[57,1395,290],{"class":263},[57,1397,345],{"class":311},[57,1399,753],{"class":267},[57,1401,766],{"class":339},[57,1403,290],{"class":263},[57,1405,771],{"class":311},[57,1407,656],{"class":267},[57,1409,371],{"class":263},[57,1411,1412],{"class":311}," any",[57,1414,1415],{"class":267},"(v ",[57,1417,377],{"class":263},[57,1419,662],{"class":263},[57,1421,380],{"class":311},[57,1423,1424],{"class":263}," for",[57,1426,1427],{"class":267}," v ",[57,1429,333],{"class":263},[57,1431,1432],{"class":267}," row)]\n",[57,1434,1435,1437,1439,1442,1445,1448,1451,1454,1457],{"class":59,"line":277},[57,1436,312],{"class":311},[57,1438,315],{"class":267},[57,1440,1441],{"class":311},"len",[57,1443,1444],{"class":267},"(data), ",[57,1446,1447],{"class":67},"\"data rows out of\"",[57,1449,1450],{"class":267},", ws.max_row ",[57,1452,1453],{"class":263},"-",[57,1455,1456],{"class":311}," 1",[57,1458,421],{"class":267},[10,1460,1461,1465],{},[39,1462,1464],{"href":1463},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-openpyxl-max-row-counting-empty-rows\u002F","Fix openpyxl max_row Counting Empty Rows"," goes further, including finding the true last row per column.",[45,1467,1469],{"id":1468},"read-only-mode-emptycell","Read-only mode: EmptyCell",[10,1471,1472,1473,1476,1477,1479,1480,1483,1484,1487,1488,1490,1491,1494],{},"Large files are often opened with ",[22,1474,1475],{},"read_only=True"," for speed. The ",[22,1478,33],{}," values are the same, but two things change. Blank cells come back as ",[22,1481,1482],{},"EmptyCell"," objects, which have a ",[22,1485,1486],{},"value"," of ",[22,1489,33],{}," but no ",[22,1492,1493],{},"coordinate",", so the diagnostic loop in Step 1 fails on them. And a read-only worksheet does not load merged ranges at all:",[14,1496,1498],{"className":254,"code":1497,"language":256,"meta":20,"style":20},"ro = load_workbook(\"sales.xlsx\", read_only=True)\nrows = ro[\"Sales\"].iter_rows(min_row=3, max_row=3)\nprint([type(c).__name__ for c in next(rows)])\nprint(\"merged ranges available:\", hasattr(ro[\"Sales\"], \"merged_cells\"))\nro.close()\n",[22,1499,1500,1522,1554,1580,1608],{"__ignoreMap":20},[57,1501,1502,1505,1507,1509,1511,1513,1516,1518,1520],{"class":59,"line":60},[57,1503,1504],{"class":267},"ro ",[57,1506,290],{"class":263},[57,1508,293],{"class":267},[57,1510,296],{"class":67},[57,1512,753],{"class":267},[57,1514,1515],{"class":339},"read_only",[57,1517,290],{"class":263},[57,1519,771],{"class":311},[57,1521,421],{"class":267},[57,1523,1524,1527,1529,1532,1534,1537,1539,1541,1544,1546,1548,1550,1552],{"class":59,"line":277},[57,1525,1526],{"class":267},"rows ",[57,1528,290],{"class":263},[57,1530,1531],{"class":267}," ro[",[57,1533,302],{"class":67},[57,1535,1536],{"class":267},"].iter_rows(",[57,1538,340],{"class":339},[57,1540,290],{"class":263},[57,1542,1543],{"class":311},"3",[57,1545,753],{"class":267},[57,1547,756],{"class":339},[57,1549,290],{"class":263},[57,1551,1543],{"class":311},[57,1553,421],{"class":267},[57,1555,1556,1558,1561,1563,1566,1568,1570,1572,1574,1577],{"class":59,"line":284},[57,1557,312],{"class":311},[57,1559,1560],{"class":267},"([",[57,1562,408],{"class":311},[57,1564,1565],{"class":267},"(c).",[57,1567,418],{"class":311},[57,1569,1424],{"class":263},[57,1571,364],{"class":267},[57,1573,333],{"class":263},[57,1575,1576],{"class":311}," next",[57,1578,1579],{"class":267},"(rows)])\n",[57,1581,1582,1584,1586,1589,1591,1594,1597,1599,1602,1605],{"class":59,"line":308},[57,1583,312],{"class":311},[57,1585,315],{"class":267},[57,1587,1588],{"class":67},"\"merged ranges available:\"",[57,1590,753],{"class":267},[57,1592,1593],{"class":311},"hasattr",[57,1595,1596],{"class":267},"(ro[",[57,1598,302],{"class":67},[57,1600,1601],{"class":267},"], ",[57,1603,1604],{"class":67},"\"merged_cells\"",[57,1606,1607],{"class":267},"))\n",[57,1609,1610],{"class":59,"line":324},[57,1611,1612],{"class":267},"ro.close()\n",[10,1614,1615,1616,1619],{},"In read-only mode, work with ",[22,1617,1618],{},"values_only=True"," and positions instead of cell objects, and if the sheet has merged cells that matter, read it once in normal mode to collect the ranges, or fill the merged values forward with pandas after loading. Close read-only workbooks explicitly; they keep the file open until you do.",[45,1621,1623],{"id":1622},"putting-it-together","Putting it together",[10,1625,1626],{},"A reader that handles all four cases, and reports what it defaulted, so nothing is silent:",[14,1628,1630],{"className":254,"code":1629,"language":256,"meta":20,"style":20},"records, notes = [], []\nfor row in ws.iter_rows(min_row=2):\n    values = [merged_value(ws, c) for c in row]\n    if all(v is None for v in values):\n        continue\n    region, rep, q1, q2, _ = values\n    if q2 is None:\n        notes.append(f\"row {row[0].row}: Q2 blank, treated as 0\")\n    records.append({\"region\": text(region), \"rep\": text(rep, \"(no rep)\"),\n                    \"total\": number(q1, 0.0) + number(q2, 0.0)})\nprint(records)\nprint(notes)\n",[22,1631,1632,1642,1660,1679,1701,1706,1715,1729,1756,1779,1802,1809],{"__ignoreMap":20},[57,1633,1634,1637,1639],{"class":59,"line":60},[57,1635,1636],{"class":267},"records, notes ",[57,1638,290],{"class":263},[57,1640,1641],{"class":267}," [], []\n",[57,1643,1644,1646,1648,1650,1652,1654,1656,1658],{"class":59,"line":277},[57,1645,327],{"class":263},[57,1647,330],{"class":267},[57,1649,333],{"class":263},[57,1651,336],{"class":267},[57,1653,340],{"class":339},[57,1655,290],{"class":263},[57,1657,345],{"class":311},[57,1659,348],{"class":267},[57,1661,1662,1665,1667,1670,1672,1674,1676],{"class":59,"line":284},[57,1663,1664],{"class":267},"    values ",[57,1666,290],{"class":263},[57,1668,1669],{"class":267}," [merged_value(ws, c) ",[57,1671,327],{"class":263},[57,1673,364],{"class":267},[57,1675,333],{"class":263},[57,1677,1678],{"class":267}," row]\n",[57,1680,1681,1683,1686,1688,1690,1692,1694,1696,1698],{"class":59,"line":308},[57,1682,388],{"class":263},[57,1684,1685],{"class":311}," all",[57,1687,1415],{"class":267},[57,1689,377],{"class":263},[57,1691,380],{"class":311},[57,1693,1424],{"class":263},[57,1695,1427],{"class":267},[57,1697,333],{"class":263},[57,1699,1700],{"class":267}," values):\n",[57,1702,1703],{"class":59,"line":324},[57,1704,1705],{"class":263},"        continue\n",[57,1707,1708,1710,1712],{"class":59,"line":351},[57,1709,779],{"class":267},[57,1711,290],{"class":263},[57,1713,1714],{"class":267}," values\n",[57,1716,1717,1719,1722,1724,1726],{"class":59,"line":385},[57,1718,388],{"class":263},[57,1720,1721],{"class":267}," q2 ",[57,1723,377],{"class":263},[57,1725,380],{"class":311},[57,1727,1728],{"class":267},":\n",[57,1730,1731,1734,1736,1739,1741,1744,1746,1749,1751,1754],{"class":59,"line":394},[57,1732,1733],{"class":267},"        notes.append(",[57,1735,795],{"class":263},[57,1737,1738],{"class":67},"\"row ",[57,1740,802],{"class":801},[57,1742,1743],{"class":267},"row[",[57,1745,105],{"class":311},[57,1747,1748],{"class":267},"].row",[57,1750,817],{"class":801},[57,1752,1753],{"class":67},": Q2 blank, treated as 0\"",[57,1755,421],{"class":267},[57,1757,1758,1761,1764,1767,1770,1773,1776],{"class":59,"line":731},[57,1759,1760],{"class":267},"    records.append({",[57,1762,1763],{"class":67},"\"region\"",[57,1765,1766],{"class":267},": text(region), ",[57,1768,1769],{"class":67},"\"rep\"",[57,1771,1772],{"class":267},": text(rep, ",[57,1774,1775],{"class":67},"\"(no rep)\"",[57,1777,1778],{"class":267},"),\n",[57,1780,1781,1784,1787,1789,1791,1794,1797,1799],{"class":59,"line":736},[57,1782,1783],{"class":67},"                    \"total\"",[57,1785,1786],{"class":267},": number(q1, ",[57,1788,843],{"class":311},[57,1790,656],{"class":267},[57,1792,1793],{"class":263},"+",[57,1795,1796],{"class":267}," number(q2, ",[57,1798,843],{"class":311},[57,1800,1801],{"class":267},")})\n",[57,1803,1804,1806],{"class":59,"line":776},[57,1805,312],{"class":311},[57,1807,1808],{"class":267},"(records)\n",[57,1810,1811,1813],{"class":59,"line":787},[57,1812,312],{"class":311},[57,1814,1815],{"class":267},"(notes)\n",[45,1817,1819],{"id":1818},"common-pitfalls","Common pitfalls",[1821,1822,1823,1845,1853,1861,1867],"ul",{},[1824,1825,1826,1833,1834,1837,1838,1840,1841,1844],"li",{},[1827,1828,1829,1830,43],"strong",{},"Wrapping everything in ",[22,1831,1832],{},"or 0"," ",[22,1835,1836],{},"value or 0"," also turns a legitimate ",[22,1839,105],{}," or empty string into the default, and hides missing data; test ",[22,1842,1843],{},"is None"," explicitly.",[1824,1846,1847,1852],{},[1827,1848,1849,1851],{},[22,1850,180],{}," on Python-written files."," There are no cached results; recalculate first or compute in Python.",[1824,1854,1855,1860],{},[1827,1856,1857,1858,43],{},"Saving a workbook loaded with ",[22,1859,180],{}," The formulas are replaced by their cached values, or lost where there were none. Never save over the original from a data-only load.",[1824,1862,1863,1866],{},[1827,1864,1865],{},"Merged ranges."," Only the top-left cell has a value.",[1824,1868,1869,1874],{},[1827,1870,1871,1872,43],{},"Looping to ",[22,1873,756],{}," Formatting extends it; skip empty rows.",[45,1876,1878],{"id":1877},"conclusion","Conclusion",[10,1880,1881,1882,1884,1885,1887,1888,43],{},"When openpyxl returns ",[22,1883,33],{},", first print which cells are empty to see the pattern. Handle genuinely blank cells with explicit defaults in small helper functions; for formula cells read with ",[22,1886,180],{},", recalculate the file first or compute the value in Python; read merged ranges from their top-left cell; and skip rows that are entirely empty instead of trusting ",[22,1889,756],{},[45,1891,1893],{"id":1892},"frequently-asked-questions","Frequently asked questions",[10,1895,1896,1899],{},[1827,1897,1898],{},"Why does openpyxl return None for a cell that has a formula?","\nWith data_only=True openpyxl returns the cached result saved by the last application that calculated the file. Files written by Python have none, so the value is None. Open and save the file in Excel or recalculate it with LibreOffice first.",[10,1901,1902,1905],{},[1827,1903,1904],{},"How do I treat empty cells as zero in openpyxl?","\nCheck for None explicitly, for example 0 if cell.value is None else cell.value. Avoid value or 0, which also replaces legitimate zeros and empty strings.",[10,1907,1908,1911],{},[1827,1909,1910],{},"Why are some cells in a merged range None?","\nA merged range stores its value only in the top-left cell; the others are MergedCell placeholders. Look up the range containing the cell and read its top-left cell.",[10,1913,1914,1917],{},[1827,1915,1916],{},"Why does my loop read empty rows at the end of the sheet?","\nws.max_row includes rows that only have formatting. Skip rows whose values are all None, or stop at the last row with a value in a key column.",[45,1919,1921],{"id":1920},"related","Related",[1821,1923,1924,1930,1935,1942,1947],{},[1824,1925,1926,1927,1929],{},"Up one level: ",[39,1928,42],{"href":41}," — the other common errors.",[1824,1931,1932,1934],{},[39,1933,1464],{"href":1463}," — the trailing-rows case in depth.",[1824,1936,1937,1941],{},[39,1938,1940],{"href":1939},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-mergedcell-value-is-read-only-in-openpyxl\u002F","Fix \"MergedCell object attribute value is read-only\" in openpyxl"," — the writing side of merged cells.",[1824,1943,1944,1946],{},[39,1945,1111],{"href":1110}," — data_only in detail.",[1824,1948,1949,1953],{},[39,1950,1952],{"href":1951},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-excel-rows-into-dictionaries-with-openpyxl\u002F","Read Excel Rows into Dictionaries with openpyxl"," — a reader built on these helpers.",[1955,1956,1957],"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 .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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":20,"searchDepth":277,"depth":277,"links":1959},[1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971],{"id":47,"depth":277,"text":48},{"id":244,"depth":277,"text":245},{"id":595,"depth":277,"text":596},{"id":865,"depth":277,"text":866},{"id":1195,"depth":277,"text":1196},{"id":1352,"depth":277,"text":1353},{"id":1468,"depth":277,"text":1469},{"id":1622,"depth":277,"text":1623},{"id":1818,"depth":277,"text":1819},{"id":1877,"depth":277,"text":1878},{"id":1892,"depth":277,"text":1893},{"id":1920,"depth":277,"text":1921},"2026-09-29","Why openpyxl hands your code None — empty cells, formulas that were never calculated, merged ranges, trailing formatted rows — and how to fix each one: find which cell is None, skip or default empty values deliberately, get formula results with data_only after a recalculation, and read merged ranges from their top-left cell.","md",[1976,1978,1980,1982],{"q":1898,"a":1977},"With data_only=True openpyxl returns the cached result saved by the last application that calculated the file. Files written by Python have none, so the value is None. Open and save the file in Excel or recalculate it with LibreOffice first.",{"q":1904,"a":1979},"Check for None explicitly, for example 0 if cell.value is None else cell.value. Avoid value or 0, which also replaces legitimate zeros and empty strings.",{"q":1910,"a":1981},"A merged range stores its value only in the top-left cell; the others are MergedCell placeholders. Look up the range containing the cell and read its top-left cell.",{"q":1916,"a":1983},"ws.max_row includes rows that only have formatting. Skip rows whose values are all None, or stop at the last row with a value in a key column.","fix nonetype errors when reading cells with openpyxl",null,{"breadcrumb":1987},[1988,1991,1994],{"name":1989,"item":1990},"Home","\u002F",{"name":1992,"item":1993},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":42,"item":41},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl",{"title":1997,"description":1998},"Fix openpyxl 'NoneType' Errors: Cell Value Is None","Fix TypeError unsupported operand NoneType and AttributeError 'NoneType' object has no attribute when reading Excel with openpyxl: empty cells, data_only formulas returning None, merged cells and trailing rows.","fix-nonetype-errors-when-reading-cells-with-openpyxl","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-nonetype-errors-when-reading-cells-with-openpyxl\u002Findex",[2002,2003],"troubleshooting","openpyxl","how-to","wjdqOs-JgdQw0qkMAhGgmxEWj1Fc2ULohuaIP5H_X9o",[2007,2011],{"title":2008,"path":2009,"stem":2010,"children":-1},"Fix #NAME? in Formulas Written from Python (XLOOKUP, TEXTJOIN, IFS)","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-name-error-for-new-excel-functions-written-by-python","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-name-error-for-new-excel-functions-written-by-python\u002Findex",{"title":2012,"path":2013,"stem":2014,"children":-1},"Fix \"Number Stored as Text\" Warnings in Excel Output","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-number-stored-as-text-warnings-in-excel-output\u002Findex",[2016,2018],{"path":2017,"title":1992},"\u002Fgetting-started-with-python-excel-automation",{"path":2019,"title":42},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors",[],[2022,2026,2029,2032,2035,2038],{"path":2023,"title":2024,"kind":2025},"\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":2027,"title":2028,"kind":2004},"\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":2030,"title":2031,"kind":2025},"\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":2033,"title":2034,"kind":2004},"\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":2036,"title":2037,"kind":2004},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fhandle-microsoft-graph-throttling-for-excel","Handle Microsoft Graph Throttling in Excel Automation",{"path":2039,"title":2040,"kind":2041},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation","Error Handling and Logging in Excel Automation","guide",1790972067192]