pypa/pip · error · InvalidLicenseExpression
Invalid licenseref: {token!r}
Error message
Invalid licenseref: {token!r} What it means
Raised by canonicalize_license_expression (licenses/__init__.py:182-184) for a LicenseRef-* token whose suffix (after 'LicenseRef-') fails the regex ^[A-Za-z0-9.-]+$ (license_ref_allowed) or which carries a '+' OR-later suffix. Custom LicenseRef identifiers are allowed beyond the SPDX built-ins, but they must be alphanumeric with dots/hyphens only and cannot be versioned with '+'.
Source
Thrown at src/pip/_vendor/packaging/licenses/__init__.py:184
if token not in EXCEPTIONS:
message = f"Unknown license exception: {token!r}"
raise InvalidLicenseExpression(message)
normalized_tokens.append(EXCEPTIONS[token]["id"])
last_license_start = False
else:
if token.endswith("+"):
final_token = token[:-1]
suffix = "+"
else:
final_token = token
suffix = ""
if final_token.startswith("licenseref-"):
license_ref_id = final_token[len("licenseref-") :]
if suffix or not license_ref_allowed.match(license_ref_id):
message = f"Invalid licenseref: {token!r}"
raise InvalidLicenseExpression(message)
normalized_tokens.append(license_refs[final_token])
else:
if final_token not in LICENSES:
message = f"Unknown license: {final_token!r}"
raise InvalidLicenseExpression(message)
normalized_tokens.append(LICENSES[final_token]["id"] + suffix)
last_license_start = True
normalized_expression = " ".join(normalized_tokens)
return cast(
"NormalizedLicenseExpression",
normalized_expression.replace("( ", "(").replace(" )", ")"),
)
View on GitHub (pinned to f399c37189)
Solutions
- Use only [A-Za-z0-9.-] after 'LicenseRef-', replacing underscores with hyphens.
- Remove any trailing '+' from a LicenseRef token.
Example fix
# before
canonicalize_license_expression('LicenseRef-My_License+')
# after
canonicalize_license_expression('LicenseRef-My-License') Defensive patterns
Strategy: validation
Validate before calling
import re
_LICENSE_REF = re.compile(r"^[A-Za-z0-9.-]+$")
def valid_license_ref(token: str) -> bool:
t = token.lower()
if not t.startswith("licenseref-"):
return True # not a ref, not our concern here
if t.endswith("+"):
return False
suffix = t[len("licenseref-"):]
return bool(_LICENSE_REF.match(suffix))
Try / catch
from packaging.licenses import canonicalize_license_expression, InvalidLicenseExpression
try:
canonicalize_license_expression(expr)
except InvalidLicenseExpression:
...
Prevention
- For custom license refs use only letters, digits, dots and hyphens.
- Never append '+' to a LicenseRef token.
When it happens
Trigger: Passing 'LicenseRef-' (empty suffix), 'LicenseRef-My_License' (underscore), 'LicenseRef-foo/bar' (slash), 'LicenseRef-foo bar' (space), or 'LicenseRef-Custom+' (the '+' suffix is rejected for refs).
Common situations: Reusing Python-style identifiers (underscores) in a custom license ref; appending '+' thinking OR-later applies to custom licenses; paths/slashes leaking into the ref.
Related errors
- Invalid license expression: {raw_license_expression!r}
- Unknown license exception: {token!r}
- Unknown license: {final_token!r}
- Invalid wheel filename {wheel.filename!r}
- Name in {wheel.filename!r} is not consistent with package na
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/363e01d82b1d7ad3.
Report an issue: GitHub.