pypa/pip · error · ExceptionGroup
invalid or unparsed metadata
Error message
invalid or unparsed metadata
What it means
Re-raised as ExceptionGroup('invalid or unparsed metadata') by Metadata.from_email after from_raw has already collected individual InvalidMetadata exceptions. It bundles all field-level problems (invalid values, unparsed fields, type mismatches) into one group so callers see every defect rather than just the first.
Source
Thrown at src/pip/_vendor/packaging/metadata.py:843
If *validate* is true, the metadata will be validated. All exceptions
related to validation will be gathered and raised as an :class:`ExceptionGroup`.
"""
raw, unparsed = parse_email(data)
if validate:
with _ErrorCollector().on_exit("unparsed") as collector:
for unparsed_key in unparsed:
if unparsed_key in _EMAIL_TO_RAW_MAPPING:
message = f"{unparsed_key!r} has invalid data"
else:
message = f"unrecognized field: {unparsed_key!r}"
collector.error(InvalidMetadata(unparsed_key, message))
try:
return cls.from_raw(raw, validate=validate)
except ExceptionGroup as exc_group:
raise ExceptionGroup(
"invalid or unparsed metadata", exc_group.exceptions
) from None
metadata_version: _Validator[_MetadataVersion] = _Validator()
""":external:ref:`core-metadata-metadata-version`
(required; validated to be a valid metadata version)"""
# `name` is not normalized/typed to NormalizedName so as to provide access to
# the original/raw name.
name: _Validator[str] = _Validator()
""":external:ref:`core-metadata-name`
(required; validated using :func:`~packaging.utils.canonicalize_name` and its
*validate* parameter)"""
version: _Validator[version_module.Version] = _Validator()
""":external:ref:`core-metadata-version` (required)"""
dynamic: _Validator[list[str] | None] = _Validator(
added="2.2",
)
""":external:ref:`core-metadata-dynamic`View on GitHub (pinned to d7d0d0a394)
Solutions
- Inspect exc.exceptions: each is an InvalidMetadata keyed by field; fix them one by one.
- Run twine check / build locally to surface all metadata issues before publishing.
- Pass validate=False if you only need best-effort parsing, then handle unparsed fields manually.
- Generate metadata with a conformant backend (setuptools, hatchling, flit) rather than hand-writing METADATA.
Example fix
# before
md = Metadata.from_email(raw, validate=True)
# after
try:
md = Metadata.from_email(raw, validate=True)
except ExceptionGroup as eg:
for sub in eg.exceptions:
print(sub)
raise Defensive patterns
Strategy: try-catch
Validate before calling
def validate_metadata(raw):
try:
Metadata.from_email(raw, validate=True)
return []
except ExceptionGroup as eg:
return eg.exceptions Try / catch
try:
md = Metadata.from_email(raw, validate=True)
except ExceptionGroup as eg:
for sub in eg.exceptions:
print(f'{sub.field}: {sub}') # InvalidMetadata has .field
raise Prevention
- Generate metadata with a conformant PEP 621 backend.
- Run twine check / build before publishing.
- Iterate over ExceptionGroup.exceptions to fix all defects at once.
- Use validate=False only for best-effort parsing and handle unparsed fields.
When it happens
Trigger: Calling from_email on METADATA with validate=True when fields are malformed (bad version, duplicate keys, invalid classifier, unknown fields, etc.). Any case where from_raw raises ExceptionGroup of InvalidMetadata instances.
Common situations: Consuming third-party package metadata; validating pyproject during build; CI linting wheels with twine check.
Related errors
- {name} has an invalid wheel, {e}
- multiple .dist-info directories found: {}
- .dist-info directory {info_dir!r} does not start with {canon
- error decoding {path!r}: {e!r}
- WHEEL is missing Wheel-Version
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/c3aae0392376f948.json.
Report an issue: GitHub.