Skip to content
Latchkey

setuptools "setup.cfg" vs "pyproject.toml" Metadata Conflict in CI

When the same field lives in both setup.cfg and the [project] table of pyproject.toml, modern setuptools refuses the ambiguity. Fields declared in [project] win, and any not marked dynamic may not be read from setup.cfg.

What this error means

A build fails with setuptools complaining a field is defined in both places, or that name/version is missing because it was left in setup.cfg while [project] exists. The build worked before [project] was added to pyproject.toml.

Build output
error: 'version' is defined both in 'pyproject.toml' and 'setup.cfg'.
# or
ValueError: 'version' must be specified in the [project] table or marked as dynamic.

Common causes

The same field declared in both files

Adding a [project] table to pyproject.toml while keeping [metadata] in setup.cfg duplicates fields. setuptools treats this as a conflict rather than merging.

A field left in setup.cfg but not marked dynamic

Once [project] exists, a field only read from setup.cfg/setup.py (like a computed version) must be listed under dynamic, or setuptools reports it missing.

How to fix it

Keep static metadata in one place

Move all static fields into [project] and remove the duplicates from setup.cfg.

pyproject.toml
[project]
name = "myapp"
version = "1.2.3"
requires-python = ">=3.10"

Mark computed fields as dynamic

If a field is still supplied by setup.cfg/setup.py or a plugin, declare it dynamic so setuptools knows to look elsewhere.

pyproject.toml
[project]
name = "myapp"
dynamic = ["version"]

[tool.setuptools.dynamic]
version = {attr = "myapp.__version__"}

How to prevent it

  • Pick one metadata home - prefer [project] in pyproject.toml.
  • List anything computed at build time under dynamic.
  • Run python -m build locally after metadata edits to catch conflicts.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →