2025, Oct 06 01:00

PyInstaller fails with spire.xls after freezing? Fix DLL loading by using --add-binary instead of --add-data

Troubleshooting spire.xls with PyInstaller: fix AttributeError in frozen executables by packaging required DLLs as binaries using --add-binary, not --add-data.

Packaging a Python tool that uses spire.xls can be deceptively simple: it runs flawlessly from an IDE, but the executable built with PyInstaller fails at runtime. The tell is an AttributeError that only appears after freezing. The good news is that this is not a logic bug in your code; it is a packaging issue tied to how PyInstaller treats different resource types.

The symptom

From source it works, from the packaged .exe it doesn’t. The executable exits with a message like:

An exception of type AttributeError occurred. Arguments: ('NoneType' object has no attribute 'Workbook_CreateWorkbook',)

Before the failure, the build was invoked with a command that tries to ship .dll files as data. For example:

pyinstaller --noconfirm --onefile --console 
  --add-data "D:\work\apps\venv\Lib\site-packages\spire\xls\lib\Spire.Xls.Base.dll;." 
  --add-data "D:\work\apps\venv\Lib\site-packages\spire\xls\bin\Spire.XLS.dll;." 
  --add-data "D:\work\apps\venv\Lib\site-packages\spire\xls\lib\libSkiaSharp.dll;." 
  "D:\work\apps\RunReports.py"

What is going on

PyInstaller treats non-binary assets and native libraries differently. The flag used above is intended for non-binary resources. To include .dll files, they must be provided as binaries. When .dlls are bundled incorrectly, runtime code that relies on them won’t load as expected, and you end up with errors during object creation or method calls.

The fix

Switch from including .dll files as data to including them as binaries. That is the entire crux of the fix.

pyinstaller 
  --add-binary ./libSkiaSharp.dll:./spire/pdf/lib 
  --add-binary ./Spire.Pdf.Base.dll:./spire/pdf/lib 
  invoice_build.py

Update the .dll names and paths to match your environment. The important part is using --add-binary for each .dll and mapping it to a destination inside the bundled application.

Why this matters

Libraries like spire.xls rely on native components. If those aren’t included as binaries during packaging, the executable won’t have access to them at runtime. Using the correct PyInstaller flag aligns the build with how native libraries are actually loaded and prevents the hard-to-trace failures that only appear after freezing.

Conclusion

If your spire.xls-powered script runs from the interpreter but breaks after packaging, look first at how you are including the .dlls. Do not ship them as data. Use --add-binary for every required .dll, verify the paths, and rebuild. This single change resolves the mismatch between development and packaged behavior and keeps your distribution predictable.

The article is based on a question from StackOverflow by goryef and an answer by Dheeraj Malik.