Fix WeasyPrint Cannot Load Library Error
pip install weasyprint succeeds, and the first import fails:
OSError: cannot load library 'libgobject-2.0-0': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libgobject-2.0-0'
Variants on other systems read cannot load library 'pango-1.0-0', cannot load library 'libpangoft2-1.0.so.0', or OSError: dlopen() failed to load a library: gobject-2.0 / gobject-2.0-0. On a developer's Mac it works; in the Docker image, the CI runner or on a Windows laptop it does not.
Root Cause
WeasyPrint is written in Python but does its text layout with Pango, a C library from the GNOME stack, reached at runtime through cffi. Pango in turn needs GLib/GObject, HarfBuzz and Fontconfig. None of these are Python packages, so pip cannot install them; they must come from the operating system. The error message names the first library the loader failed to find — usually gobject, because it is loaded first — which is why the message looks unrelated to text layout. On Windows there is no system package manager that places these DLLs on the search path, and since Python 3.8 Windows no longer searches PATH for DLL dependencies of extension modules, so even correctly installed libraries can be invisible to Python. Older WeasyPrint versions (before 53) additionally required Cairo; current versions do not, so tutorials listing cairo are out of date but harmless.
Minimal Diagnostic
Try loading each required library directly, in the same way cffi does, and print the platform details. The first library that fails is the one to install or expose.
# pip install cffi
import ctypes.util
import os
import platform
import sys
CANDIDATES = {
"gobject": ["libgobject-2.0-0", "gobject-2.0-0", "gobject-2.0", "libgobject-2.0.so.0", "libgobject-2.0.dylib"],
"pango": ["libpango-1.0-0", "pango-1.0-0", "pango-1.0", "libpango-1.0.so.0", "libpango-1.0.dylib"],
"harfbuzz": ["libharfbuzz-0", "harfbuzz", "libharfbuzz.so.0", "libharfbuzz.dylib"],
"fontconfig": ["libfontconfig-1", "fontconfig-1", "fontconfig", "libfontconfig.so.1", "libfontconfig.dylib"],
"pangoft2": ["libpangoft2-1.0-0", "pangoft2-1.0-0", "pangoft2-1.0", "libpangoft2-1.0.so.0", "libpangoft2-1.0.dylib"],
}
def diagnose() -> None:
import cffi
ffi = cffi.FFI()
print(f"python {sys.version.split()[0]} on {platform.system()} {platform.machine()}")
print("WEASYPRINT_DLL_DIRECTORIES =", os.environ.get("WEASYPRINT_DLL_DIRECTORIES"))
for name, options in CANDIDATES.items():
loaded = None
for option in options:
try:
ffi.dlopen(option)
loaded = option
break
except OSError:
continue
found = ctypes.util.find_library(name.replace("pangoft2", "pangoft2-1.0"))
print(f"{name:<11} dlopen: {loaded or 'FAILED':<24} find_library: {found}")
if __name__ == "__main__":
diagnose()
python 3.12.4 on Linux x86_64
WEASYPRINT_DLL_DIRECTORIES = None
gobject dlopen: libgobject-2.0.so.0 find_library: libgobject-2.0.so.0
pango dlopen: FAILED find_library: None
harfbuzz dlopen: libharfbuzz.so.0 find_library: libharfbuzz.so.0
fontconfig dlopen: libfontconfig.so.1 find_library: libfontconfig.so.1
pangoft2 dlopen: FAILED find_library: None
GLib, HarfBuzz and Fontconfig are present — common on servers because other software needs them — but Pango is not. Installing Pango fixes this machine.
Fix: Install Pango from the Platform's Package Source
Install the system libraries where the code runs, then reinstall nothing on the Python side — WeasyPrint finds them on the next import.
# Debian / Ubuntu (servers, WSL, GitHub Actions ubuntu runners)
sudo apt-get update
sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libharfbuzz-subset0 fontconfig
# Fedora / RHEL
sudo dnf install -y pango harfbuzz fontconfig
# Alpine
apk add --no-cache pango harfbuzz fontconfig font-dejavu
# macOS (Homebrew)
brew install pango
For containers, bake the libraries and at least one real font into the image. Slim base images have neither, and a missing font produces a second, quieter failure — rendering succeeds with boxes or a poor fallback font:
FROM python:3.12-slim
# system libraries WeasyPrint loads through cffi, plus fonts it can use
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libharfbuzz-subset0 \
fontconfig fonts-dejavu-core \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "render_reports.py"]
Then confirm from Python with a minimal render — importing alone does not exercise font discovery:
# pip install weasyprint
import sys
from pathlib import Path
def weasyprint_self_test(dest: Path = Path("out/selftest.pdf")) -> None:
try:
from weasyprint import HTML # changed: import inside the check
except OSError as exc:
raise SystemExit(f"system libraries missing: {exc}")
dest.parent.mkdir(parents=True, exist_ok=True)
HTML(string="<h1>Self-test</h1><p>Zażółć gęślą jaźń — 1,240.50 €</p>").write_pdf(dest)
assert dest.stat().st_size > 1000, "PDF suspiciously small"
print(f"WeasyPrint OK on {sys.platform}: {dest}")
if __name__ == "__main__":
weasyprint_self_test()
The accented Polish sentence and euro sign exercise font coverage and shaping; open the self-test PDF once after setting up a new image to confirm no boxes appear.
Variant Fix 1: Windows
On Windows, install the GTK/Pango stack through MSYS2, then tell WeasyPrint where the DLLs are with WEASYPRINT_DLL_DIRECTORIES, because Python does not search PATH for them:
# in an MSYS2 UCRT64 shell (install MSYS2 from msys2.org first)
pacman -S --needed mingw-w64-ucrt-x86_64-pango
# then, in PowerShell or the system environment settings
setx WEASYPRINT_DLL_DIRECTORIES "C:\msys64\ucrt64\bin"
Open a new terminal after setx so the variable is visible, and confirm with the diagnostic script. The variable accepts several directories separated by semicolons. When the environment variable cannot be set — a service account, a locked-down laptop — add the directory in code before importing WeasyPrint:
# pip install weasyprint
import os
import sys
from pathlib import Path
MSYS_BIN = Path(os.environ.get("MSYS_UCRT_BIN", r"C:\msys64\ucrt64\bin"))
if sys.platform == "win32":
if not MSYS_BIN.is_dir():
raise SystemExit(f"Pango DLL folder not found: {MSYS_BIN}")
os.add_dll_directory(str(MSYS_BIN)) # Python 3.8+: explicit DLL search path
os.environ.setdefault("WEASYPRINT_DLL_DIRECTORIES", str(MSYS_BIN))
from weasyprint import HTML # noqa: E402 (import after the DLL path is set)
Mixing 32-bit and 64-bit components is the other classic Windows failure (error 0xc1): use a 64-bit Python with the 64-bit UCRT64 or MINGW64 packages. Installing an unrelated GTK runtime from an old installer often provides incompatible DLL versions; prefer MSYS2 and remove older GTK folders from PATH.
Variant Fix 2: macOS and Apple Silicon
Homebrew on Apple Silicon installs libraries under /opt/homebrew/lib, which is not on the default library search path for Python distributions that were not built by Homebrew — notably the python.org installer and some Conda builds. The error then persists after brew install pango. Point the dynamic loader at Homebrew's library folder for the process:
# one-off, in the shell that runs the script
export DYLD_FALLBACK_LIBRARY_PATH="/opt/homebrew/lib:${DYLD_FALLBACK_LIBRARY_PATH}"
python render_reports.py
A Python running under Rosetta (x86_64) cannot load arm64 Homebrew libraries and fails with an architecture mismatch hidden inside the same cannot load library message. Check with python -c "import platform; print(platform.machine())"; it must print arm64 to use /opt/homebrew, or x86_64 to use an Intel Homebrew under /usr/local.
Reading the Error Variants
The same root problem produces several messages, and each variant points to a slightly different fix. Match the text before changing anything, because installing more packages does not help when the real issue is architecture or search path:
Keeping the diagnostic script in the repository next to the renderer pays off here: whoever sets up the next server runs one command and reads which library failed to load, instead of working backwards from a stack trace. When a deployment target cannot install system packages at all — some managed platforms — that is a decision point rather than a bug: run WeasyPrint in a container you control, or switch that job to ReportLab.
Verification
Build the self-test into the application's start-up or health check, run it in CI on every platform you deploy to, and fail early with the real cause rather than when the first customer report is due:
# .github/workflows/weasyprint.yml
name: weasyprint
on: [push]
jobs:
render:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: System libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0
- name: System libraries (macOS)
if: runner.os == 'macOS'
run: brew install pango
- run: pip install weasyprint
- run: python selftest.py
A passing matrix build proves the libraries install and load on each platform with the same commands your documentation gives. When a runner image changes and removes a library, this job fails first — not the production report run.
FAQ
Do I need to install GTK? Only the Pango stack, not the full GTK toolkit. On Windows the MSYS2 Pango package pulls in the required GLib parts without GTK widgets.
Why does it work in Jupyter but not in the service? The notebook's environment (for example Conda) ships its own Pango libraries, while the service uses a different Python that does not. Run the diagnostic from both interpreters.
Can I avoid system libraries entirely? Not with WeasyPrint. For pure-Python PDF generation use ReportLab, as in Generating PDF Reports Dynamically, or render HTML with a headless Chromium where that is already available.
How do I keep the libraries in sync with the WeasyPrint version?
Pin WeasyPrint in requirements.txt and rebuild the container image when you upgrade it. Recent WeasyPrint releases require minimum Pango versions; an old LTS base image with a new WeasyPrint can fail at import with a version error rather than a missing library, which the self-test also catches.
Is Cairo still required? Not for WeasyPrint 53 and later. Tutorials that install Cairo are from older versions; installing it does no harm but does not fix a missing Pango.
Related
- Generate PDF Reports from HTML with WeasyPrint — templates, print CSS and batching
- Fix ReportLab Unicode Font Errors — font problems in the pure-Python alternative
- Fix pdf2image Poppler Not Installed — the same missing-system-dependency pattern for Poppler
- Fix docx2pdf Error on Linux — another conversion tool with platform-specific requirements
Part of Generating PDF Reports Dynamically.