written by Eric J. Ma on 2025-03-01 | tags: python packaging pypi hatchling setuptools build-backend metadata license github actions workflow automation deployment error handling
Encountering a PyPI upload error related to license metadata? The solution is straightforward - switch from setuptools to Hatchling as your build backend. In this post, I walk through how to fix the "license-file introduced in metadata version 2.4" error by updating your pyproject.toml configuration. Along the way, I learned some new things, including the fact that modern build backends like Hatchling provide better support for PEP 621 metadata features compared to older tools like setuptools.
I use a GitHub Actions workflow to automate the release process for my Python packages. The workflow was designed to:
uv
, a blazing-fast Python package installer and resolverI chose uv
for my CI/CD pipelines because it's significantly faster than pip and provides more reliable dependency resolution. However, even with uv
's modern tooling, I encountered this error:
error: Upload failed with status code 400 Bad Request. Server says: 400 license-file introduced in metadata version 2.4
The error occurred because I was using setuptools as my build backend, which has less mature support for newer PEP 621 metadata features like the license
field with a file
specification. This led me down a path of discovery about modern Python packaging tools. Even though I was using uv
for package installation, the underlying build system still needed modernization.
pyproject.toml
filepyproject.toml
build system specification from setuptools to Hatchling:[build-system] requires = ["hatchling"] build-backend = "hatchling.build"
[project] # ... other fields ... license = { file = "LICENSE" }
That's it! The error should be resolved because Hatchling has better support for modern packaging metadata.
The Python packaging ecosystem has been evolving rapidly. Newer tools like Hatchling are built from the ground up to support modern packaging standards and provide better defaults. The error I encountered was just one symptom of using an older tool (setuptools) that's still catching up with newer metadata specifications.
@article{
ericmjl-2025-how-metadata,
author = {Eric J. Ma},
title = {How to fix PyPI upload errors related to license metadata},
year = {2025},
month = {03},
day = {01},
howpublished = {\url{https://ericmjl.github.io}},
journal = {Eric J. Ma's Blog},
url = {https://ericmjl.github.io/blog/2025/3/1/how-to-fix-pypi-upload-errors-related-to-license-metadata},
}
I send out a newsletter with tips and tools for data scientists. Come check it out at Substack.
If you would like to sponsor the coffee that goes into making my posts, please consider GitHub Sponsors!
Finally, I do free 30-minute GenAI strategy calls for teams that are looking to leverage GenAI for maximum impact. Consider booking a call on Calendly if you're interested!