[{"data":1,"prerenderedAt":2651},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror":2615,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror":2624,"children:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror":2629,"related:\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror":2630},{"id":4,"title":5,"body":6,"dateModified":2580,"datePublished":2580,"description":2581,"extension":2582,"faq":2583,"keyword":2592,"kind":2593,"meta":2594,"navigation":260,"path":2603,"seo":2604,"slug":2607,"stem":2608,"topics":2609,"type":2613,"__hash__":2614},"docs\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror\u002Findex.md","Fix \"ValueError: Cannot convert to Excel\" in openpyxl",{"type":7,"value":8,"toc":2566},"minimark",[9,13,24,41,200,205,231,235,238,419,422,426,447,638,648,759,770,774,777,1128,1143,1358,1362,1365,1669,1672,1676,1687,1883,1886,2027,2031,2034,2394,2400,2404,2435,2439,2488,2492,2501,2505,2511,2517,2523,2529,2533,2562],[10,11,12],"p",{},"Writing values cell by cell or row by row with openpyxl sometimes stops with:",[14,15,21],"pre",{"className":16,"code":18,"language":19,"meta":20},[17],"language-text","ValueError: Cannot convert [1, 2] to Excel\n","text","",[22,23,18],"code",{"__ignoreMap":20},[10,25,26,27,30,31,34,35,40],{},"— or ",[22,28,29],{},"Cannot convert {'a': 1} to Excel",", or ",[22,32,33],{},"Cannot convert \u003CNA> to Excel",". openpyxl writes only values that Excel can store in a cell, and the value named in the message is not one of them. The fix is to decide how that value should appear in the workbook and convert it before writing. This guide, part of ",[36,37,39],"a",{"href":38},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002F","Troubleshooting Common Python Excel Errors",", lists the types that fail, shows how to find them in a large dataset, and gives a conversion helper that handles them all.",[42,43,51,52,51,56,51,60,51,68,51,76,51,83,51,89,51,94,51,100,51,102,51,113,51,116,51,120,51,122,51,127,51,130,51,134,51,136,51,141,51,144,51,148,51,150,51,158,51,161,51,165,51,167,51,172,51,175,51,179,51,181,51,186,51,189,51,193,51,195],"svg",{"viewBox":44,"role":45,"ariaLabelledBy":46,"xmlns":49,"style":50},"4 0 752 310","img",[47,48],"conv-types-t","conv-types-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  ",[53,54,55],"title",{"id":47},"Which Python values openpyxl can write to a cell",[57,58,59],"desc",{"id":48},"Integers, floats, Decimal, strings, booleans, dates, datetimes, timedeltas and None write directly. Lists, tuples, dicts, sets, UUIDs and pandas NA raise ValueError Cannot convert to Excel. Timezone-aware datetimes raise a TypeError instead.",[61,62],"rect",{"x":63,"y":64,"width":65,"height":66,"fill":67},"4","0","752","310","#ffffff",[61,69],{"x":70,"y":71,"width":72,"height":73,"rx":64,"fill":67,"stroke":74,"style":75},"20.0","16.0","224.1","40.0","var(--line,#cdd5e6)","stroke-width:1px",[61,77],{"x":78,"y":71,"width":79,"height":73,"rx":64,"fill":80,"stroke":81,"style":82},"244.1","495.9","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[19,84,88],{"x":85,"y":86,"style":87},"492.1","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Writes directly",[61,90],{"x":70,"y":91,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"56.0","34.0","#e7ebef",[19,95,99],{"x":96,"y":97,"style":98},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","int, float, Decimal",[61,101],{"x":78,"y":91,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[103,104,106],"g",{"transform":105},"translate(483.1,64.0) scale(0.750)",[107,108],"path",{"d":109,"fill":110,"stroke":111,"style":112},"M5 12.5l4.5 4.5L19 7","none","var(--teal,#0f9488)","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[61,114],{"x":70,"y":115,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"90.0",[19,117,119],{"x":96,"y":118,"style":98},"112.0","str, bool, None",[61,121],{"x":78,"y":115,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[103,123,125],{"transform":124},"translate(483.1,98.0) scale(0.750)",[107,126],{"d":109,"fill":110,"stroke":111,"style":112},[61,128],{"x":70,"y":129,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"124.0",[19,131,133],{"x":96,"y":132,"style":98},"146.0","date, datetime, time, timedelta",[61,135],{"x":78,"y":129,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[103,137,139],{"transform":138},"translate(483.1,132.0) scale(0.750)",[107,140],{"d":109,"fill":110,"stroke":111,"style":112},[61,142],{"x":70,"y":143,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"158.0",[19,145,147],{"x":96,"y":146,"style":98},"180.0","list, tuple, set, dict",[61,149],{"x":78,"y":143,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[103,151,153],{"transform":152},"translate(484.1,167.0) scale(0.667)",[107,154],{"d":155,"fill":110,"stroke":156,"style":157},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[61,159],{"x":70,"y":160,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"192.0",[19,162,164],{"x":96,"y":163,"style":98},"214.0","uuid.UUID",[61,166],{"x":78,"y":160,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[103,168,170],{"transform":169},"translate(484.1,201.0) scale(0.667)",[107,171],{"d":155,"fill":110,"stroke":156,"style":157},[61,173],{"x":70,"y":174,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"226.0",[19,176,178],{"x":96,"y":177,"style":98},"248.0","pd.NA (nullable columns)",[61,180],{"x":78,"y":174,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[103,182,184],{"transform":183},"translate(484.1,235.0) scale(0.667)",[107,185],{"d":155,"fill":110,"stroke":156,"style":157},[61,187],{"x":70,"y":188,"width":72,"height":92,"rx":64,"fill":93,"stroke":74,"style":75},"260.0",[19,190,192],{"x":96,"y":191,"style":98},"282.0","datetime with tzinfo",[61,194],{"x":78,"y":188,"width":79,"height":92,"rx":64,"fill":67,"stroke":74,"style":75},[19,196,199],{"x":85,"y":197,"style":198},"281.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","no — a TypeError",[201,202,204],"h2",{"id":203},"prerequisites","Prerequisites",[14,206,210],{"className":207,"code":208,"language":209,"meta":20,"style":20},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash",[22,211,212],{"__ignoreMap":20},[213,214,217,221,225,228],"span",{"class":215,"line":216},"line",1,[213,218,220],{"class":219},"sMTad","pip",[213,222,224],{"class":223},"srMev"," install",[213,226,227],{"class":223}," openpyxl",[213,229,230],{"class":223}," pandas\n",[201,232,234],{"id":233},"step-1-reproduce-and-read-the-message","Step 1 — Reproduce and read the message",[10,236,237],{},"The message prints the offending value itself, which is usually enough to recognise where it came from:",[14,239,243],{"className":240,"code":241,"language":242,"meta":20,"style":20},"language-python shiki shiki-themes github-light github-dark-high-contrast","import uuid\n\nfrom openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nfor value in [[1, 2], {\"a\": 1}, (1, 2), {1}, uuid.UUID(int=7)]:\n    try:\n        ws[\"A1\"] = value\n    except ValueError as exc:\n        print(exc)\n","python",[22,244,245,255,262,276,281,293,304,369,378,395,410],{"__ignoreMap":20},[213,246,247,251],{"class":215,"line":216},[213,248,250],{"class":249},"s-kum","import",[213,252,254],{"class":253},"skGVy"," uuid\n",[213,256,258],{"class":215,"line":257},2,[213,259,261],{"emptyLinePlaceholder":260},true,"\n",[213,263,265,268,271,273],{"class":215,"line":264},3,[213,266,267],{"class":249},"from",[213,269,270],{"class":253}," openpyxl ",[213,272,250],{"class":249},[213,274,275],{"class":253}," Workbook\n",[213,277,279],{"class":215,"line":278},4,[213,280,261],{"emptyLinePlaceholder":260},[213,282,284,287,290],{"class":215,"line":283},5,[213,285,286],{"class":253},"wb ",[213,288,289],{"class":249},"=",[213,291,292],{"class":253}," Workbook()\n",[213,294,296,299,301],{"class":215,"line":295},6,[213,297,298],{"class":253},"ws ",[213,300,289],{"class":249},[213,302,303],{"class":253}," wb.active\n",[213,305,307,310,313,316,319,323,326,329,332,335,338,340,343,345,347,349,352,354,357,361,363,366],{"class":215,"line":306},7,[213,308,309],{"class":249},"for",[213,311,312],{"class":253}," value ",[213,314,315],{"class":249},"in",[213,317,318],{"class":253}," [[",[213,320,322],{"class":321},"sP0c6","1",[213,324,325],{"class":253},", ",[213,327,328],{"class":321},"2",[213,330,331],{"class":253},"], {",[213,333,334],{"class":223},"\"a\"",[213,336,337],{"class":253},": ",[213,339,322],{"class":321},[213,341,342],{"class":253},"}, (",[213,344,322],{"class":321},[213,346,325],{"class":253},[213,348,328],{"class":321},[213,350,351],{"class":253},"), {",[213,353,322],{"class":321},[213,355,356],{"class":253},"}, uuid.UUID(",[213,358,360],{"class":359},"sa561","int",[213,362,289],{"class":249},[213,364,365],{"class":321},"7",[213,367,368],{"class":253},")]:\n",[213,370,372,375],{"class":215,"line":371},8,[213,373,374],{"class":249},"    try",[213,376,377],{"class":253},":\n",[213,379,381,384,387,390,392],{"class":215,"line":380},9,[213,382,383],{"class":253},"        ws[",[213,385,386],{"class":223},"\"A1\"",[213,388,389],{"class":253},"] ",[213,391,289],{"class":249},[213,393,394],{"class":253}," value\n",[213,396,398,401,404,407],{"class":215,"line":397},10,[213,399,400],{"class":249},"    except",[213,402,403],{"class":321}," ValueError",[213,405,406],{"class":249}," as",[213,408,409],{"class":253}," exc:\n",[213,411,413,416],{"class":215,"line":412},11,[213,414,415],{"class":321},"        print",[213,417,418],{"class":253},"(exc)\n",[10,420,421],{},"Lists and dicts usually come from JSON — an API response where one field holds an array or a nested object. UUIDs come from databases and ORMs. Sets come from deduplicating in Python. None of them has an Excel equivalent, so openpyxl cannot guess how you want them shown.",[201,423,425],{"id":424},"step-2-the-pdna-case-nullable-columns","Step 2 — The pd.NA case: nullable columns",[10,427,428,429,325,432,325,435,438,439,442,443,446],{},"The most common trigger in pandas pipelines is quieter. Nullable dtypes — ",[22,430,431],{},"Int64",[22,433,434],{},"boolean",[22,436,437],{},"string"," — represent missing values with ",[22,440,441],{},"pd.NA",", and ",[22,444,445],{},"openpyxl.utils.dataframe.dataframe_to_rows"," passes those through unchanged:",[14,448,450],{"className":240,"code":449,"language":242,"meta":20,"style":20},"import pandas as pd\nfrom openpyxl.utils.dataframe import dataframe_to_rows\n\ndf = pd.DataFrame({\n    \"qty\": pd.array([1, None], dtype=\"Int64\"),\n    \"name\": pd.array([\"widget\", None], dtype=\"string\"),\n})\nwb = Workbook()\nws = wb.active\ntry:\n    for row in dataframe_to_rows(df, index=False, header=True):\n        ws.append(row)\nexcept ValueError as exc:\n    print(exc)                       # Cannot convert \u003CNA> to Excel\n",[22,451,452,465,477,481,491,520,545,550,558,566,573,607,613,625],{"__ignoreMap":20},[213,453,454,456,459,462],{"class":215,"line":216},[213,455,250],{"class":249},[213,457,458],{"class":253}," pandas ",[213,460,461],{"class":249},"as",[213,463,464],{"class":253}," pd\n",[213,466,467,469,472,474],{"class":215,"line":257},[213,468,267],{"class":249},[213,470,471],{"class":253}," openpyxl.utils.dataframe ",[213,473,250],{"class":249},[213,475,476],{"class":253}," dataframe_to_rows\n",[213,478,479],{"class":215,"line":264},[213,480,261],{"emptyLinePlaceholder":260},[213,482,483,486,488],{"class":215,"line":278},[213,484,485],{"class":253},"df ",[213,487,289],{"class":249},[213,489,490],{"class":253}," pd.DataFrame({\n",[213,492,493,496,499,501,503,506,509,512,514,517],{"class":215,"line":283},[213,494,495],{"class":223},"    \"qty\"",[213,497,498],{"class":253},": pd.array([",[213,500,322],{"class":321},[213,502,325],{"class":253},[213,504,505],{"class":321},"None",[213,507,508],{"class":253},"], ",[213,510,511],{"class":359},"dtype",[213,513,289],{"class":249},[213,515,516],{"class":223},"\"Int64\"",[213,518,519],{"class":253},"),\n",[213,521,522,525,527,530,532,534,536,538,540,543],{"class":215,"line":295},[213,523,524],{"class":223},"    \"name\"",[213,526,498],{"class":253},[213,528,529],{"class":223},"\"widget\"",[213,531,325],{"class":253},[213,533,505],{"class":321},[213,535,508],{"class":253},[213,537,511],{"class":359},[213,539,289],{"class":249},[213,541,542],{"class":223},"\"string\"",[213,544,519],{"class":253},[213,546,547],{"class":215,"line":306},[213,548,549],{"class":253},"})\n",[213,551,552,554,556],{"class":215,"line":371},[213,553,286],{"class":253},[213,555,289],{"class":249},[213,557,292],{"class":253},[213,559,560,562,564],{"class":215,"line":380},[213,561,298],{"class":253},[213,563,289],{"class":249},[213,565,303],{"class":253},[213,567,568,571],{"class":215,"line":397},[213,569,570],{"class":249},"try",[213,572,377],{"class":253},[213,574,575,578,581,583,586,589,591,594,596,599,601,604],{"class":215,"line":412},[213,576,577],{"class":249},"    for",[213,579,580],{"class":253}," row ",[213,582,315],{"class":249},[213,584,585],{"class":253}," dataframe_to_rows(df, ",[213,587,588],{"class":359},"index",[213,590,289],{"class":249},[213,592,593],{"class":321},"False",[213,595,325],{"class":253},[213,597,598],{"class":359},"header",[213,600,289],{"class":249},[213,602,603],{"class":321},"True",[213,605,606],{"class":253},"):\n",[213,608,610],{"class":215,"line":609},12,[213,611,612],{"class":253},"        ws.append(row)\n",[213,614,616,619,621,623],{"class":215,"line":615},13,[213,617,618],{"class":249},"except",[213,620,403],{"class":321},[213,622,406],{"class":249},[213,624,409],{"class":253},[213,626,628,631,634],{"class":215,"line":627},14,[213,629,630],{"class":321},"    print",[213,632,633],{"class":253},"(exc)                       ",[213,635,637],{"class":636},"s-wDw","# Cannot convert \u003CNA> to Excel\n",[10,639,640,641,643,644,647],{},"Convert missing values to ",[22,642,505],{}," before writing — the one \"missing\" value openpyxl understands — by casting to object first so pandas does not put ",[22,645,646],{},"NA"," back:",[14,649,651],{"className":240,"code":650,"language":242,"meta":20,"style":20},"clean = df.astype(object).where(df.notna(), None)\nwb = Workbook()\nws = wb.active\nfor row in dataframe_to_rows(clean, index=False, header=True):\n    ws.append(row)\nwb.save(\"nullable.xlsx\")\nprint([c.value for c in ws[3]])      # [None, None]\n",[22,652,653,674,682,690,717,722,732],{"__ignoreMap":20},[213,654,655,658,660,663,666,669,671],{"class":215,"line":216},[213,656,657],{"class":253},"clean ",[213,659,289],{"class":249},[213,661,662],{"class":253}," df.astype(",[213,664,665],{"class":321},"object",[213,667,668],{"class":253},").where(df.notna(), ",[213,670,505],{"class":321},[213,672,673],{"class":253},")\n",[213,675,676,678,680],{"class":215,"line":257},[213,677,286],{"class":253},[213,679,289],{"class":249},[213,681,292],{"class":253},[213,683,684,686,688],{"class":215,"line":264},[213,685,298],{"class":253},[213,687,289],{"class":249},[213,689,303],{"class":253},[213,691,692,694,696,698,701,703,705,707,709,711,713,715],{"class":215,"line":278},[213,693,309],{"class":249},[213,695,580],{"class":253},[213,697,315],{"class":249},[213,699,700],{"class":253}," dataframe_to_rows(clean, ",[213,702,588],{"class":359},[213,704,289],{"class":249},[213,706,593],{"class":321},[213,708,325],{"class":253},[213,710,598],{"class":359},[213,712,289],{"class":249},[213,714,603],{"class":321},[213,716,606],{"class":253},[213,718,719],{"class":215,"line":283},[213,720,721],{"class":253},"    ws.append(row)\n",[213,723,724,727,730],{"class":215,"line":295},[213,725,726],{"class":253},"wb.save(",[213,728,729],{"class":223},"\"nullable.xlsx\"",[213,731,673],{"class":253},[213,733,734,737,740,742,745,747,750,753,756],{"class":215,"line":306},[213,735,736],{"class":321},"print",[213,738,739],{"class":253},"([c.value ",[213,741,309],{"class":249},[213,743,744],{"class":253}," c ",[213,746,315],{"class":249},[213,748,749],{"class":253}," ws[",[213,751,752],{"class":321},"3",[213,754,755],{"class":253},"]])      ",[213,757,758],{"class":636},"# [None, None]\n",[10,760,761,764,765,769],{},[22,762,763],{},"pd.read_excel"," reads those empty cells back as missing values, so the round trip is clean. Missing data in reports generally is covered in ",[36,766,768],{"href":767},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002F","Handling Missing Data in Excel Reports",".",[201,771,773],{"id":772},"step-3-find-the-offending-values-in-a-large-dataset","Step 3 — Find the offending values in a large dataset",[10,775,776],{},"In a frame with hundreds of thousands of rows, the error names a value but not a row or column. Scan the frame for anything openpyxl cannot write and report where it is before writing anything:",[14,778,780],{"className":240,"code":779,"language":242,"meta":20,"style":20},"import datetime as dt\nimport decimal\nimport math\n\nWRITABLE = (int, float, decimal.Decimal, str, bool, dt.date, dt.datetime, dt.time, dt.timedelta, type(None))\n\ndef unwritable(frame: pd.DataFrame) -> pd.DataFrame:\n    hits = []\n    for col in frame.columns:\n        for idx, value in frame[col].items():\n            if value is pd.NA or not isinstance(value, WRITABLE) and not hasattr(value, \"item\"):\n                hits.append({\"row\": idx, \"column\": col, \"type\": type(value).__name__, \"value\": repr(value)[:40]})\n    return pd.DataFrame(hits)\n\nrecords = pd.DataFrame({\n    \"id\": [uuid.UUID(int=1), uuid.UUID(int=2)],\n    \"tags\": [[\"new\", \"vip\"], []],\n    \"meta\": [{\"source\": \"web\"}, None],\n    \"amount\": [10.5, 7.0],\n})\nprint(unwritable(records))\n",[22,781,782,794,801,808,812,855,859,871,881,893,906,953,1002,1010,1014,1024,1051,1071,1096,1115,1120],{"__ignoreMap":20},[213,783,784,786,789,791],{"class":215,"line":216},[213,785,250],{"class":249},[213,787,788],{"class":253}," datetime ",[213,790,461],{"class":249},[213,792,793],{"class":253}," dt\n",[213,795,796,798],{"class":215,"line":257},[213,797,250],{"class":249},[213,799,800],{"class":253}," decimal\n",[213,802,803,805],{"class":215,"line":264},[213,804,250],{"class":249},[213,806,807],{"class":253}," math\n",[213,809,810],{"class":215,"line":278},[213,811,261],{"emptyLinePlaceholder":260},[213,813,814,817,820,823,825,827,830,833,836,838,841,844,847,850,852],{"class":215,"line":283},[213,815,816],{"class":321},"WRITABLE",[213,818,819],{"class":249}," =",[213,821,822],{"class":253}," (",[213,824,360],{"class":321},[213,826,325],{"class":253},[213,828,829],{"class":321},"float",[213,831,832],{"class":253},", decimal.Decimal, ",[213,834,835],{"class":321},"str",[213,837,325],{"class":253},[213,839,840],{"class":321},"bool",[213,842,843],{"class":253},", dt.date, dt.datetime, dt.time, dt.timedelta, ",[213,845,846],{"class":321},"type",[213,848,849],{"class":253},"(",[213,851,505],{"class":321},[213,853,854],{"class":253},"))\n",[213,856,857],{"class":215,"line":295},[213,858,261],{"emptyLinePlaceholder":260},[213,860,861,864,868],{"class":215,"line":306},[213,862,863],{"class":249},"def",[213,865,867],{"class":866},"s_Opv"," unwritable",[213,869,870],{"class":253},"(frame: pd.DataFrame) -> pd.DataFrame:\n",[213,872,873,876,878],{"class":215,"line":371},[213,874,875],{"class":253},"    hits ",[213,877,289],{"class":249},[213,879,880],{"class":253}," []\n",[213,882,883,885,888,890],{"class":215,"line":380},[213,884,577],{"class":249},[213,886,887],{"class":253}," col ",[213,889,315],{"class":249},[213,891,892],{"class":253}," frame.columns:\n",[213,894,895,898,901,903],{"class":215,"line":397},[213,896,897],{"class":249},"        for",[213,899,900],{"class":253}," idx, value ",[213,902,315],{"class":249},[213,904,905],{"class":253}," frame[col].items():\n",[213,907,908,911,913,916,919,921,924,927,930,933,935,938,941,943,946,948,951],{"class":215,"line":412},[213,909,910],{"class":249},"            if",[213,912,312],{"class":253},[213,914,915],{"class":249},"is",[213,917,918],{"class":253}," pd.",[213,920,646],{"class":321},[213,922,923],{"class":249}," or",[213,925,926],{"class":249}," not",[213,928,929],{"class":321}," isinstance",[213,931,932],{"class":253},"(value, ",[213,934,816],{"class":321},[213,936,937],{"class":253},") ",[213,939,940],{"class":249},"and",[213,942,926],{"class":249},[213,944,945],{"class":321}," hasattr",[213,947,932],{"class":253},[213,949,950],{"class":223},"\"item\"",[213,952,606],{"class":253},[213,954,955,958,961,964,967,970,973,975,977,980,983,985,988,990,993,996,999],{"class":215,"line":609},[213,956,957],{"class":253},"                hits.append({",[213,959,960],{"class":223},"\"row\"",[213,962,963],{"class":253},": idx, ",[213,965,966],{"class":223},"\"column\"",[213,968,969],{"class":253},": col, ",[213,971,972],{"class":223},"\"type\"",[213,974,337],{"class":253},[213,976,846],{"class":321},[213,978,979],{"class":253},"(value).",[213,981,982],{"class":321},"__name__",[213,984,325],{"class":253},[213,986,987],{"class":223},"\"value\"",[213,989,337],{"class":253},[213,991,992],{"class":321},"repr",[213,994,995],{"class":253},"(value)[:",[213,997,998],{"class":321},"40",[213,1000,1001],{"class":253},"]})\n",[213,1003,1004,1007],{"class":215,"line":615},[213,1005,1006],{"class":249},"    return",[213,1008,1009],{"class":253}," pd.DataFrame(hits)\n",[213,1011,1012],{"class":215,"line":627},[213,1013,261],{"emptyLinePlaceholder":260},[213,1015,1017,1020,1022],{"class":215,"line":1016},15,[213,1018,1019],{"class":253},"records ",[213,1021,289],{"class":249},[213,1023,490],{"class":253},[213,1025,1027,1030,1033,1035,1037,1039,1042,1044,1046,1048],{"class":215,"line":1026},16,[213,1028,1029],{"class":223},"    \"id\"",[213,1031,1032],{"class":253},": [uuid.UUID(",[213,1034,360],{"class":359},[213,1036,289],{"class":249},[213,1038,322],{"class":321},[213,1040,1041],{"class":253},"), uuid.UUID(",[213,1043,360],{"class":359},[213,1045,289],{"class":249},[213,1047,328],{"class":321},[213,1049,1050],{"class":253},")],\n",[213,1052,1054,1057,1060,1063,1065,1068],{"class":215,"line":1053},17,[213,1055,1056],{"class":223},"    \"tags\"",[213,1058,1059],{"class":253},": [[",[213,1061,1062],{"class":223},"\"new\"",[213,1064,325],{"class":253},[213,1066,1067],{"class":223},"\"vip\"",[213,1069,1070],{"class":253},"], []],\n",[213,1072,1074,1077,1080,1083,1085,1088,1091,1093],{"class":215,"line":1073},18,[213,1075,1076],{"class":223},"    \"meta\"",[213,1078,1079],{"class":253},": [{",[213,1081,1082],{"class":223},"\"source\"",[213,1084,337],{"class":253},[213,1086,1087],{"class":223},"\"web\"",[213,1089,1090],{"class":253},"}, ",[213,1092,505],{"class":321},[213,1094,1095],{"class":253},"],\n",[213,1097,1099,1102,1105,1108,1110,1113],{"class":215,"line":1098},19,[213,1100,1101],{"class":223},"    \"amount\"",[213,1103,1104],{"class":253},": [",[213,1106,1107],{"class":321},"10.5",[213,1109,325],{"class":253},[213,1111,1112],{"class":321},"7.0",[213,1114,1095],{"class":253},[213,1116,1118],{"class":215,"line":1117},20,[213,1119,549],{"class":253},[213,1121,1123,1125],{"class":215,"line":1122},21,[213,1124,736],{"class":321},[213,1126,1127],{"class":253},"(unwritable(records))\n",[10,1129,1130,1131,1134,1135,1138,1139,1142],{},"The ",[22,1132,1133],{},"hasattr(value, \"item\")"," test lets NumPy scalars through — openpyxl writes ",[22,1136,1137],{},"numpy.int64"," and ",[22,1140,1141],{},"numpy.float64"," fine — while flagging everything that would fail. The report points at columns, which is what you need: the fix is almost always per column, not per cell.",[42,1144,51,1150,51,1153,51,1156,51,1161,51,1165,51,1169,51,1175,51,1179,51,1183,51,1187,51,1191,51,1195,51,1199,51,1202,51,1206,51,1209,51,1213,51,1215,51,1218,51,1220,51,1222,51,1224,51,1227,51,1230,51,1233,51,1235,51,1238,51,1242,51,1246,51,1248,51,1251,51,1253,51,1256,51,1259,51,1262,51,1264,51,1266,51,1268,51,1270,51,1272,51,1274,51,1276,51,1279,51,1281,51,1284,51,1286,51,1288,51,1290,51,1293,51,1295,51,1298,51,1300,51,1303,51,1306,51,1310,51,1312,51,1314,51,1316,51,1318,51,1320,51,1322,51,1324,51,1327,51,1329,51,1333,51,1335,51,1337,51,1339,51,1342,51,1344,51,1347,51,1349,51,1352],{"viewBox":1145,"role":45,"ariaLabelledBy":1146,"xmlns":49,"style":1149},"114 0 531 250",[1147,1148],"conv-report-t","conv-report-d","width:100%;max-width:531px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[53,1151,1152],{"id":1147},"The report of values openpyxl cannot write",[57,1154,1155],{"id":1148},"The scan lists each problem cell with its row, column, type and value. The id column holds UUID objects, the tags column holds Python lists and the meta column holds a dict, while amount is writable and does not appear.",[61,1157],{"x":1158,"y":64,"width":1159,"height":1160,"fill":67},"114","531","250",[61,1162],{"x":1163,"y":71,"width":96,"height":1164,"rx":64,"fill":93,"stroke":74,"style":75},"201.2","24.0",[61,1166],{"x":1167,"y":71,"width":1168,"height":1164,"rx":64,"fill":93,"stroke":74,"style":75},"233.2","47.6",[19,1170,1174],{"x":1171,"y":1172,"style":1173},"257.0","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[61,1176],{"x":1177,"y":71,"width":1178,"height":1164,"rx":64,"fill":93,"stroke":74,"style":75},"280.8","69.8",[19,1180,1182],{"x":1181,"y":1172,"style":1173},"315.7","B",[61,1184],{"x":1185,"y":71,"width":1186,"height":1164,"rx":64,"fill":93,"stroke":74,"style":75},"350.5","55.1",[19,1188,1190],{"x":1189,"y":1172,"style":1173},"378.1","C",[61,1192],{"x":1193,"y":71,"width":1194,"height":1164,"rx":64,"fill":93,"stroke":74,"style":75},"405.6","153.1",[19,1196,1198],{"x":1197,"y":1172,"style":1173},"482.2","D",[61,1200],{"x":1163,"y":73,"width":96,"height":1201,"rx":64,"fill":93,"stroke":74,"style":75},"28.0",[19,1203,322],{"x":1204,"y":1205,"style":1173},"217.2","59.0",[61,1207],{"x":1167,"y":73,"width":1168,"height":1201,"rx":64,"fill":80,"stroke":81,"style":1208},"stroke-width:1.5px",[19,1210,1212],{"x":1171,"y":1205,"style":1211},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","row",[61,1214],{"x":1177,"y":73,"width":1178,"height":1201,"rx":64,"fill":80,"stroke":81,"style":1208},[19,1216,1217],{"x":1181,"y":1205,"style":1211},"column",[61,1219],{"x":1185,"y":73,"width":1186,"height":1201,"rx":64,"fill":80,"stroke":81,"style":1208},[19,1221,846],{"x":1189,"y":1205,"style":1211},[61,1223],{"x":1193,"y":73,"width":1194,"height":1201,"rx":64,"fill":80,"stroke":81,"style":1208},[19,1225,1226],{"x":1197,"y":1205,"style":1211},"value",[61,1228],{"x":1163,"y":1229,"width":96,"height":1201,"rx":64,"fill":93,"stroke":74,"style":75},"68.0",[19,1231,328],{"x":1204,"y":1232,"style":1173},"87.0",[61,1234],{"x":1167,"y":1229,"width":1168,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1236,64],{"x":1171,"y":1232,"style":1237},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[61,1239],{"x":1177,"y":1229,"width":1178,"height":1201,"rx":64,"fill":1240,"stroke":1241,"style":1208},"#fdefd8","var(--gold,#b4740a)",[19,1243,1245],{"x":1181,"y":1232,"style":1244},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","id",[61,1247],{"x":1185,"y":1229,"width":1186,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1249,1250],{"x":1189,"y":1232,"style":1237},"UUID",[61,1252],{"x":1193,"y":1229,"width":1194,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1254,1255],{"x":1197,"y":1232,"style":1237},"UUID('0000…0001')",[61,1257],{"x":1163,"y":1258,"width":96,"height":1201,"rx":64,"fill":93,"stroke":74,"style":75},"96.0",[19,1260,752],{"x":1204,"y":1261,"style":1173},"115.0",[61,1263],{"x":1167,"y":1258,"width":1168,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1265,322],{"x":1171,"y":1261,"style":1237},[61,1267],{"x":1177,"y":1258,"width":1178,"height":1201,"rx":64,"fill":1240,"stroke":1241,"style":1208},[19,1269,1245],{"x":1181,"y":1261,"style":1244},[61,1271],{"x":1185,"y":1258,"width":1186,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1273,1250],{"x":1189,"y":1261,"style":1237},[61,1275],{"x":1193,"y":1258,"width":1194,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1277,1278],{"x":1197,"y":1261,"style":1237},"UUID('0000…0002')",[61,1280],{"x":1163,"y":129,"width":96,"height":1201,"rx":64,"fill":93,"stroke":74,"style":75},[19,1282,63],{"x":1204,"y":1283,"style":1173},"143.0",[61,1285],{"x":1167,"y":129,"width":1168,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1287,64],{"x":1171,"y":1283,"style":1237},[61,1289],{"x":1177,"y":129,"width":1178,"height":1201,"rx":64,"fill":1240,"stroke":1241,"style":1208},[19,1291,1292],{"x":1181,"y":1283,"style":1244},"tags",[61,1294],{"x":1185,"y":129,"width":1186,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1296,1297],{"x":1189,"y":1283,"style":1237},"list",[61,1299],{"x":1193,"y":129,"width":1194,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1301,1302],{"x":1197,"y":1283,"style":1237},"['new', 'vip']",[61,1304],{"x":1163,"y":1305,"width":96,"height":1201,"rx":64,"fill":93,"stroke":74,"style":75},"152.0",[19,1307,1309],{"x":1204,"y":1308,"style":1173},"171.0","5",[61,1311],{"x":1167,"y":1305,"width":1168,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1313,322],{"x":1171,"y":1308,"style":1237},[61,1315],{"x":1177,"y":1305,"width":1178,"height":1201,"rx":64,"fill":1240,"stroke":1241,"style":1208},[19,1317,1292],{"x":1181,"y":1308,"style":1244},[61,1319],{"x":1185,"y":1305,"width":1186,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1321,1297],{"x":1189,"y":1308,"style":1237},[61,1323],{"x":1193,"y":1305,"width":1194,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1325,1326],{"x":1197,"y":1308,"style":1237},"[]",[61,1328],{"x":1163,"y":146,"width":96,"height":1201,"rx":64,"fill":93,"stroke":74,"style":75},[19,1330,1332],{"x":1204,"y":1331,"style":1173},"199.0","6",[61,1334],{"x":1167,"y":146,"width":1168,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1336,64],{"x":1171,"y":1331,"style":1237},[61,1338],{"x":1177,"y":146,"width":1178,"height":1201,"rx":64,"fill":1240,"stroke":1241,"style":1208},[19,1340,1341],{"x":1181,"y":1331,"style":1244},"meta",[61,1343],{"x":1185,"y":146,"width":1186,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1345,1346],{"x":1189,"y":1331,"style":1237},"dict",[61,1348],{"x":1193,"y":146,"width":1194,"height":1201,"rx":64,"fill":67,"stroke":74,"style":75},[19,1350,1351],{"x":1197,"y":1331,"style":1237},"{'source': 'web'}",[19,1353,1357],{"x":1354,"y":1355,"style":1356},"380.0","230.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","problems cluster by column — convert each column once rather than cell by cell",[201,1359,1361],{"id":1360},"step-4-convert-each-kind-deliberately","Step 4 — Convert each kind deliberately",[10,1363,1364],{},"Once you know which columns hold which types, choose a representation that serves the reader:",[14,1366,1368],{"className":240,"code":1367,"language":242,"meta":20,"style":20},"import json\n\ndef for_excel(frame: pd.DataFrame) -> pd.DataFrame:\n    out = frame.copy()\n    for col in out.columns:\n        out[col] = out[col].map(lambda v: (\n            None if v is pd.NA or (isinstance(v, float) and math.isnan(v)) else\n            str(v) if isinstance(v, uuid.UUID) else\n            \", \".join(map(str, v)) if isinstance(v, (list, tuple, set)) else\n            json.dumps(v, ensure_ascii=False, sort_keys=True) if isinstance(v, dict) else\n            v\n        ))\n    return out\n\nready = for_excel(records)\nwb = Workbook()\nws = wb.active\nfor row in dataframe_to_rows(ready, index=False, header=True):\n    ws.append(row)\nwb.save(\"records.xlsx\")\nprint(pd.read_excel(\"records.xlsx\").iloc[0].tolist())\n",[22,1369,1370,1377,1381,1390,1400,1411,1427,1466,1488,1530,1565,1570,1575,1582,1586,1596,1604,1612,1639,1643,1652],{"__ignoreMap":20},[213,1371,1372,1374],{"class":215,"line":216},[213,1373,250],{"class":249},[213,1375,1376],{"class":253}," json\n",[213,1378,1379],{"class":215,"line":257},[213,1380,261],{"emptyLinePlaceholder":260},[213,1382,1383,1385,1388],{"class":215,"line":264},[213,1384,863],{"class":249},[213,1386,1387],{"class":866}," for_excel",[213,1389,870],{"class":253},[213,1391,1392,1395,1397],{"class":215,"line":278},[213,1393,1394],{"class":253},"    out ",[213,1396,289],{"class":249},[213,1398,1399],{"class":253}," frame.copy()\n",[213,1401,1402,1404,1406,1408],{"class":215,"line":283},[213,1403,577],{"class":249},[213,1405,887],{"class":253},[213,1407,315],{"class":249},[213,1409,1410],{"class":253}," out.columns:\n",[213,1412,1413,1416,1418,1421,1424],{"class":215,"line":295},[213,1414,1415],{"class":253},"        out[col] ",[213,1417,289],{"class":249},[213,1419,1420],{"class":253}," out[col].map(",[213,1422,1423],{"class":249},"lambda",[213,1425,1426],{"class":253}," v: (\n",[213,1428,1429,1432,1435,1438,1440,1442,1444,1446,1448,1451,1454,1456,1458,1460,1463],{"class":215,"line":306},[213,1430,1431],{"class":321},"            None",[213,1433,1434],{"class":249}," if",[213,1436,1437],{"class":253}," v ",[213,1439,915],{"class":249},[213,1441,918],{"class":253},[213,1443,646],{"class":321},[213,1445,923],{"class":249},[213,1447,822],{"class":253},[213,1449,1450],{"class":321},"isinstance",[213,1452,1453],{"class":253},"(v, ",[213,1455,829],{"class":321},[213,1457,937],{"class":253},[213,1459,940],{"class":249},[213,1461,1462],{"class":253}," math.isnan(v)) ",[213,1464,1465],{"class":249},"else\n",[213,1467,1468,1471,1474,1477,1479,1482,1484,1486],{"class":215,"line":371},[213,1469,1470],{"class":321},"            str",[213,1472,1473],{"class":253},"(v) ",[213,1475,1476],{"class":249},"if",[213,1478,929],{"class":321},[213,1480,1481],{"class":253},"(v, uuid.",[213,1483,1250],{"class":321},[213,1485,937],{"class":253},[213,1487,1465],{"class":249},[213,1489,1490,1493,1496,1499,1501,1503,1506,1508,1510,1513,1515,1517,1520,1522,1525,1528],{"class":215,"line":380},[213,1491,1492],{"class":223},"            \", \"",[213,1494,1495],{"class":253},".join(",[213,1497,1498],{"class":321},"map",[213,1500,849],{"class":253},[213,1502,835],{"class":321},[213,1504,1505],{"class":253},", v)) ",[213,1507,1476],{"class":249},[213,1509,929],{"class":321},[213,1511,1512],{"class":253},"(v, (",[213,1514,1297],{"class":321},[213,1516,325],{"class":253},[213,1518,1519],{"class":321},"tuple",[213,1521,325],{"class":253},[213,1523,1524],{"class":321},"set",[213,1526,1527],{"class":253},")) ",[213,1529,1465],{"class":249},[213,1531,1532,1535,1538,1540,1542,1544,1547,1549,1551,1553,1555,1557,1559,1561,1563],{"class":215,"line":397},[213,1533,1534],{"class":253},"            json.dumps(v, ",[213,1536,1537],{"class":359},"ensure_ascii",[213,1539,289],{"class":249},[213,1541,593],{"class":321},[213,1543,325],{"class":253},[213,1545,1546],{"class":359},"sort_keys",[213,1548,289],{"class":249},[213,1550,603],{"class":321},[213,1552,937],{"class":253},[213,1554,1476],{"class":249},[213,1556,929],{"class":321},[213,1558,1453],{"class":253},[213,1560,1346],{"class":321},[213,1562,937],{"class":253},[213,1564,1465],{"class":249},[213,1566,1567],{"class":215,"line":412},[213,1568,1569],{"class":253},"            v\n",[213,1571,1572],{"class":215,"line":609},[213,1573,1574],{"class":253},"        ))\n",[213,1576,1577,1579],{"class":215,"line":615},[213,1578,1006],{"class":249},[213,1580,1581],{"class":253}," out\n",[213,1583,1584],{"class":215,"line":627},[213,1585,261],{"emptyLinePlaceholder":260},[213,1587,1588,1591,1593],{"class":215,"line":1016},[213,1589,1590],{"class":253},"ready ",[213,1592,289],{"class":249},[213,1594,1595],{"class":253}," for_excel(records)\n",[213,1597,1598,1600,1602],{"class":215,"line":1026},[213,1599,286],{"class":253},[213,1601,289],{"class":249},[213,1603,292],{"class":253},[213,1605,1606,1608,1610],{"class":215,"line":1053},[213,1607,298],{"class":253},[213,1609,289],{"class":249},[213,1611,303],{"class":253},[213,1613,1614,1616,1618,1620,1623,1625,1627,1629,1631,1633,1635,1637],{"class":215,"line":1073},[213,1615,309],{"class":249},[213,1617,580],{"class":253},[213,1619,315],{"class":249},[213,1621,1622],{"class":253}," dataframe_to_rows(ready, ",[213,1624,588],{"class":359},[213,1626,289],{"class":249},[213,1628,593],{"class":321},[213,1630,325],{"class":253},[213,1632,598],{"class":359},[213,1634,289],{"class":249},[213,1636,603],{"class":321},[213,1638,606],{"class":253},[213,1640,1641],{"class":215,"line":1098},[213,1642,721],{"class":253},[213,1644,1645,1647,1650],{"class":215,"line":1117},[213,1646,726],{"class":253},[213,1648,1649],{"class":223},"\"records.xlsx\"",[213,1651,673],{"class":253},[213,1653,1654,1656,1659,1661,1664,1666],{"class":215,"line":1122},[213,1655,736],{"class":321},[213,1657,1658],{"class":253},"(pd.read_excel(",[213,1660,1649],{"class":223},[213,1662,1663],{"class":253},").iloc[",[213,1665,64],{"class":321},[213,1667,1668],{"class":253},"].tolist())\n",[10,1670,1671],{},"UUIDs become their standard text form, lists become a comma-separated string — readable and searchable with Excel's filter — and dicts become JSON text, which keeps the structure for anyone who needs to parse it later. Sets are joined too, but sort them first if the order should be stable between runs.",[201,1673,1675],{"id":1674},"step-5-split-structures-that-people-need-to-analyse","Step 5 — Split structures that people need to analyse",[10,1677,1678,1679,1682,1683,1686],{},"Joining a list into one cell is right for labels. When readers need to count or filter by the parts — orders with the \"vip\" tag, totals per tag — give each part its own row instead, with ",[22,1680,1681],{},"explode",", or its own column with ",[22,1684,1685],{},"json_normalize"," for dicts:",[14,1688,1690],{"className":240,"code":1689,"language":242,"meta":20,"style":20},"tags = records[[\"id\", \"tags\"]].explode(\"tags\").dropna(subset=[\"tags\"])\ntags[\"id\"] = tags[\"id\"].astype(str)\nmeta = pd.json_normalize(records[\"meta\"].apply(lambda m: m or {}).tolist()).add_prefix(\"meta_\")\nwith pd.ExcelWriter(\"records_split.xlsx\") as writer:\n    tags.to_excel(writer, sheet_name=\"Tags\", index=False)\n    pd.concat([records[[\"amount\"]], meta], axis=1).to_excel(writer, sheet_name=\"Details\", index=False)\nprint(pd.read_excel(\"records_split.xlsx\", sheet_name=\"Tags\"))\n",[22,1691,1692,1731,1754,1786,1804,1827,1865],{"__ignoreMap":20},[213,1693,1694,1697,1699,1702,1705,1707,1710,1713,1715,1718,1721,1723,1726,1728],{"class":215,"line":216},[213,1695,1696],{"class":253},"tags ",[213,1698,289],{"class":249},[213,1700,1701],{"class":253}," records[[",[213,1703,1704],{"class":223},"\"id\"",[213,1706,325],{"class":253},[213,1708,1709],{"class":223},"\"tags\"",[213,1711,1712],{"class":253},"]].explode(",[213,1714,1709],{"class":223},[213,1716,1717],{"class":253},").dropna(",[213,1719,1720],{"class":359},"subset",[213,1722,289],{"class":249},[213,1724,1725],{"class":253},"[",[213,1727,1709],{"class":223},[213,1729,1730],{"class":253},"])\n",[213,1732,1733,1736,1738,1740,1742,1745,1747,1750,1752],{"class":215,"line":257},[213,1734,1735],{"class":253},"tags[",[213,1737,1704],{"class":223},[213,1739,389],{"class":253},[213,1741,289],{"class":249},[213,1743,1744],{"class":253}," tags[",[213,1746,1704],{"class":223},[213,1748,1749],{"class":253},"].astype(",[213,1751,835],{"class":321},[213,1753,673],{"class":253},[213,1755,1756,1759,1761,1764,1767,1770,1772,1775,1778,1781,1784],{"class":215,"line":264},[213,1757,1758],{"class":253},"meta ",[213,1760,289],{"class":249},[213,1762,1763],{"class":253}," pd.json_normalize(records[",[213,1765,1766],{"class":223},"\"meta\"",[213,1768,1769],{"class":253},"].apply(",[213,1771,1423],{"class":249},[213,1773,1774],{"class":253}," m: m ",[213,1776,1777],{"class":249},"or",[213,1779,1780],{"class":253}," {}).tolist()).add_prefix(",[213,1782,1783],{"class":223},"\"meta_\"",[213,1785,673],{"class":253},[213,1787,1788,1791,1794,1797,1799,1801],{"class":215,"line":278},[213,1789,1790],{"class":249},"with",[213,1792,1793],{"class":253}," pd.ExcelWriter(",[213,1795,1796],{"class":223},"\"records_split.xlsx\"",[213,1798,937],{"class":253},[213,1800,461],{"class":249},[213,1802,1803],{"class":253}," writer:\n",[213,1805,1806,1809,1812,1814,1817,1819,1821,1823,1825],{"class":215,"line":283},[213,1807,1808],{"class":253},"    tags.to_excel(writer, ",[213,1810,1811],{"class":359},"sheet_name",[213,1813,289],{"class":249},[213,1815,1816],{"class":223},"\"Tags\"",[213,1818,325],{"class":253},[213,1820,588],{"class":359},[213,1822,289],{"class":249},[213,1824,593],{"class":321},[213,1826,673],{"class":253},[213,1828,1829,1832,1835,1838,1841,1843,1845,1848,1850,1852,1855,1857,1859,1861,1863],{"class":215,"line":295},[213,1830,1831],{"class":253},"    pd.concat([records[[",[213,1833,1834],{"class":223},"\"amount\"",[213,1836,1837],{"class":253},"]], meta], ",[213,1839,1840],{"class":359},"axis",[213,1842,289],{"class":249},[213,1844,322],{"class":321},[213,1846,1847],{"class":253},").to_excel(writer, ",[213,1849,1811],{"class":359},[213,1851,289],{"class":249},[213,1853,1854],{"class":223},"\"Details\"",[213,1856,325],{"class":253},[213,1858,588],{"class":359},[213,1860,289],{"class":249},[213,1862,593],{"class":321},[213,1864,673],{"class":253},[213,1866,1867,1869,1871,1873,1875,1877,1879,1881],{"class":215,"line":306},[213,1868,736],{"class":321},[213,1870,1658],{"class":253},[213,1872,1796],{"class":223},[213,1874,325],{"class":253},[213,1876,1811],{"class":359},[213,1878,289],{"class":249},[213,1880,1816],{"class":223},[213,1882,854],{"class":253},[10,1884,1885],{},"The tags sheet has one row per tag, which a pivot table can count directly — something no amount of formatting can do for a joined string.",[42,1887,51,1892,51,1895,51,1898,51,1901,51,1908,51,1916,51,1920,51,1924,51,1928,51,1931,51,1936,51,1943,51,1947,51,1951,51,1955,51,1959,51,1962,51,1965,51,1969,51,1972,51,1975,51,1978,51,1981,51,1984,51,1987,51,1991,51,1994,51,1998,51,2002,51,2005,51,2007,51,2010,51,2014,51,2017,51,2020,51,2024],{"viewBox":1888,"role":45,"ariaLabelledBy":1889,"xmlns":49,"style":50},"4 0 752 238",[1890,1891],"conv-choose-t","conv-choose-d",[53,1893,1894],{"id":1890},"How to represent a value openpyxl cannot write",[57,1896,1897],{"id":1891},"Missing values from nullable columns become None. Identifiers such as UUIDs become their text form. Lists that serve as labels are joined into one string; lists people analyse are exploded into rows. Nested dicts are stored as JSON text or flattened into columns with json_normalize.",[61,1899],{"x":63,"y":64,"width":65,"height":1900,"fill":67},"238",[61,1902],{"x":1903,"y":71,"width":1904,"height":1905,"rx":1906,"fill":80,"stroke":81,"style":1907},"175.3","409.3","75.5","12","stroke-width:2px",[103,1909,1911],{"transform":1910},"translate(368.0,29.0) scale(1.000)",[107,1912],{"d":1913,"fill":110,"stroke":1914,"style":1915},"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,1917,1919],{"x":1354,"y":1905,"style":1918},"font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What is the value, and what will readers do with it?",[215,1921],{"x1":1354,"y1":1922,"x2":1354,"y2":1923,"stroke":81,"style":1907},"91.5","111.5",[215,1925],{"x1":1926,"y1":1923,"x2":1927,"y2":1923,"stroke":81,"style":1907},"104.0","656.0",[215,1929],{"x1":1926,"y1":1923,"x2":1926,"y2":1930,"stroke":81,"style":1907},"157.5",[1932,1933],"polygon",{"points":1934,"fill":1935},"104.0,161.5 99.0,152.5 109.0,152.5","#5b5cf0",[61,1937],{"x":1938,"y":1939,"width":1940,"height":1941,"rx":1942,"fill":67,"stroke":74,"style":82},"76.3","121.5","55.5","21.0","10",[19,1944,441],{"x":1926,"y":1945,"style":1946},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle",[61,1948],{"x":70,"y":1949,"width":1950,"height":1205,"rx":1906,"fill":93,"stroke":74,"style":1907},"163.5","168.0",[19,1952,505],{"x":1926,"y":1953,"style":1954},"189.0","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle",[19,1956,1958],{"x":1926,"y":1957,"style":198},"208.5","an empty cell",[215,1960],{"x1":1961,"y1":1923,"x2":1961,"y2":1930,"stroke":81,"style":1907},"288.0",[1932,1963],{"points":1964,"fill":1935},"288.0,161.5 283.0,152.5 293.0,152.5",[61,1966],{"x":1967,"y":1939,"width":1968,"height":1941,"rx":1942,"fill":67,"stroke":74,"style":82},"229.7","116.6",[19,1970,1971],{"x":1961,"y":1945,"style":1946},"UUID or object id",[61,1973],{"x":1974,"y":1949,"width":1950,"height":1205,"rx":1906,"fill":80,"stroke":81,"style":1907},"204.0",[19,1976,1977],{"x":1961,"y":1953,"style":1918},"str(value)",[19,1979,1980],{"x":1961,"y":1957,"style":198},"text identifier",[215,1982],{"x1":1983,"y1":1923,"x2":1983,"y2":1930,"stroke":81,"style":1907},"472.0",[1932,1985],{"points":1986,"fill":1935},"472.0,161.5 467.0,152.5 477.0,152.5",[61,1988],{"x":1989,"y":1939,"width":1990,"height":1941,"rx":1942,"fill":67,"stroke":74,"style":82},"425.3","93.5",[19,1992,1993],{"x":1983,"y":1945,"style":1946},"list as a label",[61,1995],{"x":1996,"y":1949,"width":1950,"height":1205,"rx":1906,"fill":1997,"stroke":111,"style":1907},"388.0","#d9f4f1",[19,1999,2001],{"x":1983,"y":1953,"style":2000},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","join",[19,2003,2004],{"x":1983,"y":1957,"style":198},"\"new, vip\"",[215,2006],{"x1":1927,"y1":1923,"x2":1927,"y2":1930,"stroke":81,"style":1907},[1932,2008],{"points":2009,"fill":1935},"656.0,161.5 651.0,152.5 661.0,152.5",[61,2011],{"x":2012,"y":1939,"width":2013,"height":1941,"rx":1942,"fill":67,"stroke":74,"style":82},"586.1","139.8",[19,2015,2016],{"x":1927,"y":1945,"style":1946},"list or dict to analyse",[61,2018],{"x":2019,"y":1949,"width":1950,"height":1205,"rx":1906,"fill":1240,"stroke":1241,"style":1907},"572.0",[19,2021,2023],{"x":1927,"y":1953,"style":2022},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","explode \u002F normalize",[19,2025,2026],{"x":1927,"y":1957,"style":198},"rows or columns",[201,2028,2030],{"id":2029},"step-6-make-the-error-point-at-the-cell","Step 6 — Make the error point at the cell",[10,2032,2033],{},"Code that writes rows from many sources — API pages, database cursors, several frames — benefits from a small wrapper that converts what it can and, when something still fails, says exactly where. The bare openpyxl message never includes the row or column; this one does:",[14,2035,2037],{"className":240,"code":2036,"language":242,"meta":20,"style":20},"def append_row(ws, values, row_label=None):\n    converted = []\n    for position, value in enumerate(values, start=1):\n        if value is pd.NA:\n            value = None\n        elif isinstance(value, uuid.UUID):\n            value = str(value)\n        converted.append(value)\n    try:\n        ws.append(converted)\n    except ValueError as exc:\n        bad = next(i for i, v in enumerate(converted, start=1)\n                   if not isinstance(v, WRITABLE) and not hasattr(v, \"item\"))\n        raise ValueError(f\"row {row_label or ws.max_row + 1}, column {bad}: {exc}\") from exc\n\nwb = Workbook()\nws = wb.active\nappend_row(ws, [\"ok\", uuid.UUID(int=5), pd.NA])\ntry:\n    append_row(ws, [\"order-17\", 3, [\"a\", \"b\"]], row_label=\"order-17\")\nexcept ValueError as exc:\n    print(exc)                       # row order-17, column 3: Cannot convert ['a', 'b'] to Excel\n",[22,2038,2039,2055,2064,2088,2103,2113,2127,2139,2144,2150,2155,2165,2198,2225,2290,2294,2302,2310,2334,2340,2374,2384],{"__ignoreMap":20},[213,2040,2041,2043,2046,2049,2051,2053],{"class":215,"line":216},[213,2042,863],{"class":249},[213,2044,2045],{"class":866}," append_row",[213,2047,2048],{"class":253},"(ws, values, row_label",[213,2050,289],{"class":249},[213,2052,505],{"class":321},[213,2054,606],{"class":253},[213,2056,2057,2060,2062],{"class":215,"line":257},[213,2058,2059],{"class":253},"    converted ",[213,2061,289],{"class":249},[213,2063,880],{"class":253},[213,2065,2066,2068,2071,2073,2076,2079,2082,2084,2086],{"class":215,"line":264},[213,2067,577],{"class":249},[213,2069,2070],{"class":253}," position, value ",[213,2072,315],{"class":249},[213,2074,2075],{"class":321}," enumerate",[213,2077,2078],{"class":253},"(values, ",[213,2080,2081],{"class":359},"start",[213,2083,289],{"class":249},[213,2085,322],{"class":321},[213,2087,606],{"class":253},[213,2089,2090,2093,2095,2097,2099,2101],{"class":215,"line":278},[213,2091,2092],{"class":249},"        if",[213,2094,312],{"class":253},[213,2096,915],{"class":249},[213,2098,918],{"class":253},[213,2100,646],{"class":321},[213,2102,377],{"class":253},[213,2104,2105,2108,2110],{"class":215,"line":283},[213,2106,2107],{"class":253},"            value ",[213,2109,289],{"class":249},[213,2111,2112],{"class":321}," None\n",[213,2114,2115,2118,2120,2123,2125],{"class":215,"line":295},[213,2116,2117],{"class":249},"        elif",[213,2119,929],{"class":321},[213,2121,2122],{"class":253},"(value, uuid.",[213,2124,1250],{"class":321},[213,2126,606],{"class":253},[213,2128,2129,2131,2133,2136],{"class":215,"line":306},[213,2130,2107],{"class":253},[213,2132,289],{"class":249},[213,2134,2135],{"class":321}," str",[213,2137,2138],{"class":253},"(value)\n",[213,2140,2141],{"class":215,"line":371},[213,2142,2143],{"class":253},"        converted.append(value)\n",[213,2145,2146,2148],{"class":215,"line":380},[213,2147,374],{"class":249},[213,2149,377],{"class":253},[213,2151,2152],{"class":215,"line":397},[213,2153,2154],{"class":253},"        ws.append(converted)\n",[213,2156,2157,2159,2161,2163],{"class":215,"line":412},[213,2158,400],{"class":249},[213,2160,403],{"class":321},[213,2162,406],{"class":249},[213,2164,409],{"class":253},[213,2166,2167,2170,2172,2175,2178,2180,2183,2185,2187,2190,2192,2194,2196],{"class":215,"line":609},[213,2168,2169],{"class":253},"        bad ",[213,2171,289],{"class":249},[213,2173,2174],{"class":321}," next",[213,2176,2177],{"class":253},"(i ",[213,2179,309],{"class":249},[213,2181,2182],{"class":253}," i, v ",[213,2184,315],{"class":249},[213,2186,2075],{"class":321},[213,2188,2189],{"class":253},"(converted, ",[213,2191,2081],{"class":359},[213,2193,289],{"class":249},[213,2195,322],{"class":321},[213,2197,673],{"class":253},[213,2199,2200,2203,2205,2207,2209,2211,2213,2215,2217,2219,2221,2223],{"class":215,"line":615},[213,2201,2202],{"class":249},"                   if",[213,2204,926],{"class":249},[213,2206,929],{"class":321},[213,2208,1453],{"class":253},[213,2210,816],{"class":321},[213,2212,937],{"class":253},[213,2214,940],{"class":249},[213,2216,926],{"class":249},[213,2218,945],{"class":321},[213,2220,1453],{"class":253},[213,2222,950],{"class":223},[213,2224,854],{"class":253},[213,2226,2227,2230,2232,2234,2237,2240,2244,2247,2249,2252,2255,2258,2261,2264,2266,2269,2271,2273,2275,2278,2280,2283,2285,2287],{"class":215,"line":627},[213,2228,2229],{"class":249},"        raise",[213,2231,403],{"class":321},[213,2233,849],{"class":253},[213,2235,2236],{"class":249},"f",[213,2238,2239],{"class":223},"\"row ",[213,2241,2243],{"class":2242},"sSjpA","{",[213,2245,2246],{"class":253},"row_label ",[213,2248,1777],{"class":249},[213,2250,2251],{"class":253}," ws.max_row ",[213,2253,2254],{"class":249},"+",[213,2256,2257],{"class":321}," 1",[213,2259,2260],{"class":2242},"}",[213,2262,2263],{"class":223},", column ",[213,2265,2243],{"class":2242},[213,2267,2268],{"class":253},"bad",[213,2270,2260],{"class":2242},[213,2272,337],{"class":223},[213,2274,2243],{"class":2242},[213,2276,2277],{"class":253},"exc",[213,2279,2260],{"class":2242},[213,2281,2282],{"class":223},"\"",[213,2284,937],{"class":253},[213,2286,267],{"class":249},[213,2288,2289],{"class":253}," exc\n",[213,2291,2292],{"class":215,"line":1016},[213,2293,261],{"emptyLinePlaceholder":260},[213,2295,2296,2298,2300],{"class":215,"line":1026},[213,2297,286],{"class":253},[213,2299,289],{"class":249},[213,2301,292],{"class":253},[213,2303,2304,2306,2308],{"class":215,"line":1053},[213,2305,298],{"class":253},[213,2307,289],{"class":249},[213,2309,303],{"class":253},[213,2311,2312,2315,2318,2321,2323,2325,2327,2330,2332],{"class":215,"line":1073},[213,2313,2314],{"class":253},"append_row(ws, [",[213,2316,2317],{"class":223},"\"ok\"",[213,2319,2320],{"class":253},", uuid.UUID(",[213,2322,360],{"class":359},[213,2324,289],{"class":249},[213,2326,1309],{"class":321},[213,2328,2329],{"class":253},"), pd.",[213,2331,646],{"class":321},[213,2333,1730],{"class":253},[213,2335,2336,2338],{"class":215,"line":1098},[213,2337,570],{"class":249},[213,2339,377],{"class":253},[213,2341,2342,2345,2348,2350,2352,2355,2357,2359,2362,2365,2368,2370,2372],{"class":215,"line":1117},[213,2343,2344],{"class":253},"    append_row(ws, [",[213,2346,2347],{"class":223},"\"order-17\"",[213,2349,325],{"class":253},[213,2351,752],{"class":321},[213,2353,2354],{"class":253},", [",[213,2356,334],{"class":223},[213,2358,325],{"class":253},[213,2360,2361],{"class":223},"\"b\"",[213,2363,2364],{"class":253},"]], ",[213,2366,2367],{"class":359},"row_label",[213,2369,289],{"class":249},[213,2371,2347],{"class":223},[213,2373,673],{"class":253},[213,2375,2376,2378,2380,2382],{"class":215,"line":1122},[213,2377,618],{"class":249},[213,2379,403],{"class":321},[213,2381,406],{"class":249},[213,2383,409],{"class":253},[213,2385,2387,2389,2391],{"class":215,"line":2386},22,[213,2388,630],{"class":321},[213,2390,633],{"class":253},[213,2392,2393],{"class":636},"# row order-17, column 3: Cannot convert ['a', 'b'] to Excel\n",[10,2395,2396,2397,2399],{},"The wrapper deliberately converts only the unambiguous cases — missing values and identifiers — and lets lists and dicts fail loudly. Those need a decision about representation (Step 4 or Step 5), and silently joining them would hide a data change from whoever reads the report. Passing a business key such as an order number as ",[22,2398,2367],{}," makes the message useful to someone who does not know the code.",[201,2401,2403],{"id":2402},"or-let-pandas-do-the-writing","Or let pandas do the writing",[10,2405,2406,2407,2410,2411,2413,2414,2416,2417,2419,2420,2423,2424,2426,2427,2430,2431,769],{},"If you do not need openpyxl's cell-level control, ",[22,2408,2409],{},"DataFrame.to_excel"," avoids the error entirely: pandas converts unsupported objects to strings before passing them to the engine, and handles ",[22,2412,441],{}," itself. The trade-off is that you get Python's ",[22,2415,992],{},"-style text — ",[22,2418,1302],{}," with brackets and quotes, dicts in Python syntax rather than JSON — so an explicit conversion like Step 4 still produces a cleaner sheet. For example, a list ",[22,2421,2422],{},"[1, 'x']"," lands in the cell as the text ",[22,2425,2422],{},", and a dict as ",[22,2428,2429],{},"{'k': 1}"," — valid in Python, awkward for anyone reading the report. The writing options are compared in ",[36,2432,2434],{"href":2433},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter\u002F","openpyxl vs xlsxwriter vs pandas.ExcelWriter",[201,2436,2438],{"id":2437},"common-pitfalls","Common pitfalls",[2440,2441,2442,2450,2468,2474],"ul",{},[2443,2444,2445,2449],"li",{},[2446,2447,2448],"strong",{},"Converting cell by cell after the error."," Wrap one conversion step around the whole frame, as in Step 4, rather than catching the exception per cell.",[2443,2451,2452,2455,2456,2459,2460,2463,2464,769],{},[2446,2453,2454],{},"Timezone-aware datetimes."," They fail with a ",[22,2457,2458],{},"TypeError"," rather than this ",[22,2461,2462],{},"ValueError","; the fix is different — see ",[36,2465,2467],{"href":2466},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-excel-does-not-support-timezones-in-datetimes\u002F","Fix \"Excel does not support datetimes with timezones\" in pandas",[2443,2469,2470,2473],{},[2446,2471,2472],{},"Very long strings."," A JSON-serialised dict can exceed Excel's limit of 32,767 characters per cell; Excel truncates or complains. Store large payloads elsewhere and write a reference.",[2443,2475,2476,2479,2480,2483,2484,769],{},[2446,2477,2478],{},"Control characters in the text."," Strings from APIs can contain characters Excel rejects, raising ",[22,2481,2482],{},"IllegalCharacterError"," — the next thing to fail after this one; see ",[36,2485,2487],{"href":2486},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-illegalcharactererror-when-writing-excel-with-openpyxl\u002F","Fix IllegalCharacterError When Writing Excel with openpyxl",[201,2489,2491],{"id":2490},"conclusion","Conclusion",[10,2493,2494,2495,2497,2498,2500],{},"\"Cannot convert … to Excel\" means a value has no cell representation: a list, tuple, set, dict, UUID or ",[22,2496,441],{},". Scan the frame to find which columns hold such values, then convert each column deliberately — ",[22,2499,505],{}," for missing values, text for identifiers, joined strings for labels, exploded rows or normalised columns for structures people analyse, JSON for everything else — before writing with openpyxl.",[201,2502,2504],{"id":2503},"frequently-asked-questions","Frequently asked questions",[10,2506,2507,2510],{},[2446,2508,2509],{},"What types can openpyxl write to a cell?","\nNumbers (int, float, Decimal), strings, booleans, dates, datetimes, times, timedeltas and None — plus formulas as strings starting with =. Anything else raises ValueError.",[10,2512,2513,2516],{},[2446,2514,2515],{},"Why does pd.NA cause the error when NaN does not?","\nopenpyxl knows how to write None and float NaN, but pd.NA is a separate pandas object it does not recognise. Nullable columns such as Int64 and string use pd.NA for missing values, so convert them to None before writing.",[10,2518,2519,2522],{},[2446,2520,2521],{},"Why does pandas to_excel work when openpyxl directly fails?","\npandas converts unsupported objects to their string form before handing them to the engine. Writing cells yourself with openpyxl skips that step, so you must convert the values.",[10,2524,2525,2528],{},[2446,2526,2527],{},"Should I write a list as text or split it across cells?","\nIf people need to filter or sum the parts, split them into columns or rows. If the list is just a label — tags, codes — join it into one string with a clear separator.",[201,2530,2532],{"id":2531},"related","Related",[2440,2534,2535,2541,2548,2555],{},[2443,2536,2537,2538,2540],{},"Up one level: ",[36,2539,39],{"href":38}," — every common error explained.",[2443,2542,2543,2547],{},[36,2544,2546],{"href":2545},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmoving-data-between-excel-and-databases\u002Ffetch-api-data-into-excel-with-python-requests\u002F","Fetch API Data into Excel with Python requests"," — where nested JSON usually comes from.",[2443,2549,2550,2554],{},[36,2551,2553],{"href":2552},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fopenpyxl-append-data-to-existing-excel-sheet\u002F","openpyxl: Append Data to an Existing Excel Sheet"," — row writing with openpyxl.",[2443,2556,2557,2561],{},[36,2558,2560],{"href":2559},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-excel-data-types-with-pandas\u002F","Check Excel Data Types with pandas"," — catching type surprises early.",[2563,2564,2565],"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-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}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":257,"depth":257,"links":2567},[2568,2569,2570,2571,2572,2573,2574,2575,2576,2577,2578,2579],{"id":203,"depth":257,"text":204},{"id":233,"depth":257,"text":234},{"id":424,"depth":257,"text":425},{"id":772,"depth":257,"text":773},{"id":1360,"depth":257,"text":1361},{"id":1674,"depth":257,"text":1675},{"id":2029,"depth":257,"text":2030},{"id":2402,"depth":257,"text":2403},{"id":2437,"depth":257,"text":2438},{"id":2490,"depth":257,"text":2491},{"id":2503,"depth":257,"text":2504},{"id":2531,"depth":257,"text":2532},"2026-09-23","openpyxl accepts only values Excel can store — numbers, text, booleans, dates, None. Lists, dicts, sets, UUIDs and pandas' NA raise \"Cannot convert … to Excel\". Find the offending values, convert them deliberately, and write DataFrames without surprises.","md",[2584,2586,2588,2590],{"q":2509,"a":2585},"Numbers (int, float, Decimal), strings, booleans, dates, datetimes, times, timedeltas and None — plus formulas as strings starting with =. Anything else raises ValueError.",{"q":2515,"a":2587},"openpyxl knows how to write None and float NaN, but pd.NA is a separate pandas object it does not recognise. Nullable columns such as Int64 and string use pd.NA for missing values, so convert them to None before writing.",{"q":2521,"a":2589},"pandas converts unsupported objects to their string form before handing them to the engine. Writing cells yourself with openpyxl skips that step, so you must convert the values.",{"q":2527,"a":2591},"If people need to filter or sum the parts, split them into columns or rows. If the list is just a label — tags, codes — join it into one string with a clear separator.","fix valueerror cannot convert to excel in openpyxl",null,{"breadcrumb":2595},[2596,2599,2602],{"name":2597,"item":2598},"Home","\u002F",{"name":2600,"item":2601},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":39,"item":38},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror",{"title":2605,"description":2606},"Fix openpyxl ValueError: Cannot convert [..] \u002F \u003CNA> to Excel","Fix openpyxl \"ValueError Cannot convert to Excel\" for lists, dicts, sets, UUIDs and pandas NA — locate the bad cells, convert or serialise them, clean dataframe_to_rows output, and a safe cell-writing helper.","fix-cannot-convert-to-excel-valueerror","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror\u002Findex",[2610,2611,2612],"troubleshooting","writing-excel","openpyxl","how-to","u7wLm8fS2tdTq6ur7kJAzZtbyR9PRM5HT2U7Hd1_TI4",[2616,2620],{"title":2617,"path":2618,"stem":2619,"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":2621,"path":2622,"stem":2623,"children":-1},"Fix \"Could Not Convert String to Float\" in Excel Data","\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-could-not-convert-string-to-float-in-excel-data","getting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-could-not-convert-string-to-float-in-excel-data\u002Findex",[2625,2627],{"path":2626,"title":2600},"\u002Fgetting-started-with-python-excel-automation",{"path":2628,"title":39},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors",[],[2631,2635,2638,2641,2644,2647],{"path":2632,"title":2633,"kind":2634},"\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":2636,"title":2637,"kind":2613},"\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":2639,"title":2640,"kind":2634},"\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":2642,"title":2643,"kind":2613},"\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":2645,"title":2646,"kind":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fhandle-microsoft-graph-throttling-for-excel","Handle Microsoft Graph Throttling in Excel Automation",{"path":2648,"title":2649,"kind":2650},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation","Error Handling and Logging in Excel Automation","guide",1790972065468]