2025, Nov 09 13:00

Cython 3.1.2 linker error: multiple definitions from non-static helpers and how to fix

Hit a Cython 3.1.2 linker error: multiple definitions from non-static helpers? See what changed, how to fix it, and the 3.1.1 workaround restore clean builds.

Linker error with Cython-generated C files: what changed and how to fix it

Building a native binary from multiple Cythonized modules can suddenly break with an unexpected linker error on one machine while working flawlessly on another. A concrete example: the workflow works on Ubuntu 22.04 with Python 3.10 and Cython 0.29.37, but fails on Kubuntu 20.04 with Python 3.8 and Cython 3.1.2. The failure manifests as a linker complaint about multiple definitions.

multiple definitions of '__pyx_CommonTypesMetaclass_get_module'

If you regenerated the C files with the newer toolchain and then compiled them together with GCC, the build stops at the link step. The underlying reason is specific to the C code emitted by a particular Cython release.

Minimal reproduction of the symptom

Consider two C source files produced from independent Python modules. They each define the same helper symbol as a global (non-static) function. When you link them into a single binary or library, the linker finds duplicate global symbols and errors out.

alpha.c

int aux_get_module(void) {
    return 0;
}

beta.c

int aux_get_module(void) {
    return 0;
}

Compiling and linking them together yields the classic multiple-definition error.

gcc -c alpha.c -o alpha.o
gcc -c beta.c -o beta.o
gcc alpha.o beta.o -o app
multiple definitions of '__pyx_CommonTypesMetaclass_get_module'

What is actually going on

This is a bug in Cython 3.1.2: a helper function that should have had internal linkage was emitted without the static keyword. When separate Cython-generated files define the same non-static helper, linking them together produces a clash. The discrepancy across machines comes from different Cython versions being used: Ubuntu 22.04 used Cython 0.29.37, while Kubuntu 20.04 used Cython 3.1.2, and the latter introduced the regression.

The issue is tracked here: bug report, and the corresponding fix has already been merged. The symbol name that shows up in the error (__pyx_CommonTypesMetaclass_get_module) is a concrete instance of such a helper emitted without static.

Workaround you can apply today

Until the next Cython release includes the fix, pin Cython to 3.1.1. Regenerate the C sources with that version and rebuild. This avoids the regression that 3.1.2 introduced.

How the correct code should look

To illustrate the intent, the helper functions in each generated C file must be given internal linkage so they do not collide across translation units. With static, both files compile and link cleanly because each definition is private to its object file.

alpha.c

static int aux_get_module(void) {
    return 0;
}

beta.c

static int aux_get_module(void) {
    return 0;
}

Why this matters

This kind of failure appears when you combine multiple Cython-generated C files into a single library or executable. If you build such monolithic targets, small changes in how helper symbols are emitted can make the difference between a clean build and a blocked release. Recognizing that the error is tied to the Cython version rather than your own code saves time and avoids chasing false leads in GCC, Python, or the OS.

Takeaways

If a previously working multi-file Cython build starts failing with a message like “multiple definitions of '__pyx_CommonTypesMetaclass_get_module'” and you are using Cython 3.1.2, you are most likely hitting the known bug. Regenerate your C code with Cython 3.1.1 and proceed, and plan to upgrade again once the next release contains the fix from the upstream patch.

The article is based on a question from StackOverflow by Simon T. and an answer by DavidW.