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.

The stack under import weasyprint WeasyPrint and its Python dependencies such as cffi, tinycss2 and pydyf are installed by pip. Below them, cffi loads shared libraries at runtime. Pango and PangoFT2 perform text layout, HarfBuzz shapes glyphs, Fontconfig finds fonts and GLib with GObject provides the base object system. All of these must come from the operating system package manager, MSYS2 on Windows, or Homebrew on macOS. WeasyPrint + tinycss2 + pydyf installed by pip pip install weasyprint cffi dlopen loads shared libraries at runtime fails if any library below is missing Pango and PangoFT2 text layout OS package HarfBuzz glyph shaping OS package Fontconfig font discovery OS package GLib / GObject loaded first: named in the error OS package — usually the first name in the message

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.

Getting Pango by platform On Debian and Ubuntu apt installs libpango and related libraries and no extra step is needed. On Alpine apk installs pango, and fonts must be added explicitly. On macOS Homebrew installs pango, and Apple Silicon Python must match the Homebrew architecture. On Windows MSYS2 installs Pango and WEASYPRINT_DLL_DIRECTORIES or os.add_dll_directory must point at its bin folder. Slim Docker images need both the libraries and a font package installed in the image. Platform Install Extra step Debian / Ubuntu apt libpango-1.0-0 none Alpine apk pango harfbuzz add a font package macOS brew install pango match Python arch Windows MSYS2 pango package set DLL directory Slim Docker apt in the Dockerfile libs + fonts

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:

Which fix a given error message needs The root asks what the error says. A missing library name on Linux or macOS means the package is not installed, fixed with the platform package manager. Error 0x7e on Windows means the DLLs exist but are not on Python's DLL search path, fixed with WEASYPRINT_DLL_DIRECTORIES. Error 0xc1 or an incompatible architecture means a 32-bit and 64-bit or arm64 and x86_64 mismatch, fixed by matching Python and library builds. No error but boxes in the PDF means fonts are missing, fixed by installing fonts. What exactly does the error say? copy the full OSError text name not found (Linux/mac) Not installed apt / apk / brew error 0x7e (Windows) Not on DLL path set the DLL directory 0xc1 or wrong arch Architecture mismatch match 64-bit or arm64 no error, boxes Missing fonts install a font package

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.

Part of Generating PDF Reports Dynamically.

/html>