Fix the win32com gen_py AttributeError (CLSIDToClassMap)
A script that has driven Excel through pywin32 for months stops at the first line:
AttributeError: module 'win32com.gen_py.00020813-0000-0000-C000-000000000046x0x1x9' has no attribute 'CLSIDToClassMap'
Variants end in 'CLSIDToPackageMap', or appear as "This COM object can not automate the makepy process". Nothing is wrong with Excel or with your code. win32com.client.gencache.EnsureDispatch keeps a cache of Python modules generated from Excel's type library, and one of those modules is broken: half-written because a run was interrupted, left over from a different Excel version, or written by a different Python. Deleting the broken entry fixes it; this page shows how to find it, how to do that safely in a script, and how to keep it from coming back. It is part of Troubleshooting Common Python Excel Errors.
Prerequisites
pywin32 and Excel on Windows:
pip install pywin32
The code that talks to Excel only runs on Windows. The cache-cleaning function further down is plain Python and is demonstrated here against a sample folder, so its behaviour can be checked anywhere.
What gen_py is
EnsureDispatch("Excel.Application") gives early binding: pywin32 reads Excel's type library once, generates a Python module describing every object, method and constant, and imports that module on later runs. That is what makes win32com.client.constants.xlUp and fast, case-correct method calls work. The generated module lives in a folder named after the type library:
00020813-…-000000000046is the ID of Excel's type library, the long name in the error message;x0x1x9is the locale (0) and the library's version (1.9 for current Excel).
The folder sits inside the gen_py directory. Unless a gen_py folder exists inside the installed win32com package, pywin32 puts it in the user's temporary directory, in a subfolder per Python version, such as %LOCALAPPDATA%\Temp\gen_py\3.12. Different users, and the same user under different Python versions, each have their own cache.
Step 1 — Find the cache folder
Ask pywin32 where it keeps the cache rather than guessing; it prints the path it will use:
import win32com
print(win32com.__gen_path__)
Run it with the same interpreter the failing script uses: the path includes the Python version, and a scheduled job running under a service account has its own temporary directory, so its cache is somewhere else entirely. On the command line, python -m win32com.client.gencache -d lists the cached type libraries, and it fails with the same AttributeError when the Excel entry is the broken one, which confirms the diagnosis.
Step 2 — Delete the Excel entry, not everything
The cache holds entries for every COM library used on the machine, Outlook and Word included. Removing only the Excel entry is enough, and leaves the others alone. The entry can be a folder (for large libraries such as Excel) or a single .py file, and the index file dicts.dat should be removed with it so pywin32 does not look for the deleted entry:
import shutil
from pathlib import Path
EXCEL_TYPELIB = "00020813-0000-0000-C000-000000000046"
def clear_excel_cache(gen_path):
"""Remove generated Excel wrappers and the cache index from a gen_py folder."""
gen = Path(gen_path)
removed = []
for entry in gen.glob(f"{EXCEL_TYPELIB}x*"):
if entry.is_dir():
shutil.rmtree(entry)
else:
entry.unlink()
removed.append(entry.name)
index = gen / "dicts.dat"
if index.exists():
index.unlink()
removed.append(index.name)
return removed
Here it runs against a sample cache laid out the way pywin32 lays it out, with Excel's broken folder, an Outlook entry and the index:
sample = Path("gen_py/3.12")
(sample / f"{EXCEL_TYPELIB}x0x1x9").mkdir(parents=True, exist_ok=True)
(sample / f"{EXCEL_TYPELIB}x0x1x9" / "__init__.py").write_text("") # the truncated module
(sample / "00062FFF-0000-0000-C000-000000000046x0x9x6.py").write_text("# Outlook")
(sample / "dicts.dat").write_bytes(b"index")
(sample / "__init__.py").write_text("")
print("removed:", clear_excel_cache(sample))
print("left:", sorted(p.name for p in sample.iterdir()))
On Windows, call it with win32com.__gen_path__, with no Excel automation running in the meantime, then run the script again. EnsureDispatch regenerates the module on first use, which takes a few seconds once.
Step 3 — Rebuild and verify
With the entry gone, the next EnsureDispatch regenerates it. A short check confirms early binding works again, because constants are only available with a generated module:
import win32com.client as win32
excel = win32.gencache.EnsureDispatch("Excel.Application")
print(type(excel)) # <class 'win32com.gen_py....Application'>
print(win32.constants.xlUp) # -4162, from the regenerated module
excel.Quit()
The regenerated folder appears under the same name as the one you deleted, and dicts.dat is written again next to it. If the error returns immediately, the new module is being generated by a Python other than the one you checked, or another process is still holding the old module in memory; close every Python process that talks to Excel and try again.
If a script only needs to get running and does not use constants, win32com.client.dynamic.Dispatch("Excel.Application") uses late binding and never touches the cache. It is slower per call and method names are resolved at run time, so it is a workaround, not the fix.
Why it breaks, and how to prevent it
The common causes, in the order they turn up:
- An interrupted first run. Generation takes a few seconds; a script killed during it, or two scripts starting at once, leaves a partial module. Warm the cache once, interactively, after installing Python or pywin32.
- Excel updates. A new Excel version can change the type library version, and a cache built against the old one no longer matches. Clearing it after major Office updates avoids surprises.
- Several Python installations. Each Python version writes its own subfolder, but a copied virtual environment or a
gen_pyfolder inside a packaged application can carry a cache from another machine. Delete it from the package. - Shared service accounts. Scheduled jobs running as one account share one cache; a job that crashes mid-generation breaks the others. Run Excel COM Automation from Task Scheduler covers running these jobs reliably.
When a job fails on a server and works on your laptop, the first question is which Python and which cache the job actually uses. Log both at the start of every run; it costs nothing and answers the question from the job's own output:
import logging
import sys
from importlib.metadata import version
import win32com
logging.info("python %s at %s", sys.version.split()[0], sys.executable)
logging.info("pywin32 %s, gen_py cache at %s", version("pywin32"), win32com.__gen_path__)
Two jobs that print different cache paths do not share a cache, so clearing one does not fix the other. A service account's temporary directory is also not the one you see when logged in, which is why clearing "the" gen_py folder by hand sometimes appears to do nothing.
A scheduled job can recover by itself: catch the AttributeError from EnsureDispatch, clear the Excel entry, and try once more:
import win32com
import win32com.client as win32
def excel_app():
try:
return win32.gencache.EnsureDispatch("Excel.Application")
except AttributeError:
clear_excel_cache(win32com.__gen_path__)
return win32.gencache.EnsureDispatch("Excel.Application")
The retry has to happen in a fresh process if the broken module was already imported; if the second attempt still fails, exit with an error and let the scheduler rerun the job.
Common pitfalls
- Deleting all of gen_py while Excel scripts run. Clear it between runs; a script generating a module while another deletes the folder recreates the problem.
- Clearing the wrong cache.
win32com.__gen_path__depends on the Python that runs it; check it with the same interpreter the job uses. - Reaching for
dynamic.Dispatchpermanently. It avoids the cache but also loses constants and early binding; fix the cache instead. - Packaging the cache. Tools that freeze a Python app can include a
gen_pyfolder built on the developer's machine; exclude it.
Conclusion
The CLSIDToClassMap AttributeError means pywin32's generated module for Excel's type library is incomplete or stale. Find the cache with win32com.__gen_path__, delete the entries starting with Excel's type-library ID, 00020813-…, together with dicts.dat, and let EnsureDispatch rebuild them on the next run. Warm the cache once after installs and Office updates, avoid concurrent first runs, and let scheduled jobs clear the entry and retry.
Frequently asked questions
Where is the win32com gen_py folder? Print win32com.gen_path. By default it is a gen_py folder inside the user's temporary directory, with a subfolder per Python version, unless a gen_py folder exists inside the installed win32com package.
Is it safe to delete the gen_py folder? Yes. It only holds generated wrappers, which pywin32 recreates on demand. Deleting only the Excel entry and dicts.dat is enough and leaves wrappers for other applications in place.
What does the folder name 00020813-…x0x1x9 mean? It is the ID of Excel's type library followed by the locale and version of the library. The generated Python module for Excel is stored under that name.
Should I use Dispatch instead of EnsureDispatch to avoid the error? dynamic.Dispatch avoids the cache but loses constants and early binding. Use it as a temporary workaround and clear the cache to fix the real problem.
Related
- Up one level: Troubleshooting Common Python Excel Errors — the other common errors.
- Automating Excel with COM and pywin32 — EnsureDispatch and early binding.
- Handle COM Errors and Excel Dialog Prompts in Python — the errors Excel itself raises.
- Close Excel Cleanly and Avoid Orphan COM Processes — the other thing that breaks scheduled COM jobs.
- Run Excel COM Automation from Task Scheduler — running these jobs unattended.
More on errors & fixes
- FixHandle Permission Denied When Writing Excel in Python
- How-toWhat openpyxl Loses When You Save an Existing Workbook
- FixFix Excel Serial Numbers Showing Instead of Dates
- How-toHandle Daylight Saving Time in Excel Timestamps
- How-toHandle Microsoft Graph Throttling in Excel Automation
- GuideError Handling and Logging in Excel Automation