{"id":"3bf24b0167703c6a","repo":"pypa/pip","slug":"invalid-license-expression-raw-license-expressio","errorCode":null,"errorMessage":"Invalid license expression: {raw_license_expression!r}","messagePattern":"Invalid license expression: (.+?)","errorType":"validation","errorClass":"InvalidLicenseExpression","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/licenses/__init__.py","lineNumber":104,"sourceCode":"    .. doctest::\n\n        >>> from packaging.licenses import canonicalize_license_expression\n        >>> canonicalize_license_expression(\"mit\")\n        'MIT'\n        >>> canonicalize_license_expression(\"mit and (apache-2.0 or bsd-2-clause)\")\n        'MIT AND (Apache-2.0 OR BSD-2-Clause)'\n        >>> canonicalize_license_expression(\"(mit\")\n        Traceback (most recent call last):\n          ...\n        InvalidLicenseExpression: Invalid license expression: '(mit'\n        >>> canonicalize_license_expression(\"Use-it-after-midnight\")\n        Traceback (most recent call last):\n          ...\n        InvalidLicenseExpression: Unknown license: 'Use-it-after-midnight'\n    \"\"\"\n    if not raw_license_expression:\n        message = f\"Invalid license expression: {raw_license_expression!r}\"\n        raise InvalidLicenseExpression(message)\n\n    # Pad any parentheses so tokenization can be achieved by merely splitting on\n    # whitespace.\n    license_expression = raw_license_expression.replace(\"(\", \" ( \").replace(\")\", \" ) \")\n    licenseref_prefix = \"LicenseRef-\"\n    license_refs = {\n        ref.lower(): \"LicenseRef-\" + ref[len(licenseref_prefix) :]\n        for ref in license_expression.split()\n        if ref.lower().startswith(licenseref_prefix.lower())\n    }\n\n    # Normalize to lower case so we can look up licenses/exceptions\n    # and so boolean operators are Python-compatible.\n    license_expression = license_expression.lower()\n\n    tokens = license_expression.split()\n\n    # Rather than implementing a parenthesis/boolean logic parser, create an","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/licenses/__init__.py#L86-L122","documentation":"Raised by `canonicalize_license_expression` when the input is empty/falsy. An empty license-expression is invalid because it carries no license information; the SPDX/PEP 639 grammar requires at least one license token. This is the earliest validation check in the canonicalizer.","triggerScenarios":"Calling `canonicalize_license_expression('')`, `canonicalize_license_expression(None)` (after coercion), or passing a whitespace-only string that evaluates falsy. Hit by build backends (hatchling, setuptools) reading an empty `license = \"\"` from `pyproject.toml`.","commonSituations":"A `pyproject.toml` with `license = \"\"` or a missing license field that defaulted to empty; a tool that builds the license string from optional config and produces `''`; migration from the deprecated `license-file` table leaving an empty value.","solutions":["Provide a real SPDX expression such as `license = \"MIT\"` in pyproject.toml","If you genuinely have no license to declare, omit the field rather than passing empty","Validate `if not raw_license_expression: raise` upstream of canonicalize"],"exampleFix":"// before\n[project]\nlicense = \"\"\n// after\n[project]\nlicense = \"MIT\"","handlingStrategy":"validation","validationCode":"def validate_license_expression(expr: str) -> str:\n    if not expr:\n        raise ValueError('license expression must not be empty')\n    return expr","typeGuard":"def is_nonempty_license_expression(s: object) -> bool:\n    return isinstance(s, str) and bool(s.strip())","tryCatchPattern":"from packaging.licenses import canonicalize_license_expression, InvalidLicenseExpression\ntry:\n    norm = canonicalize_license_expression(expr)\nexcept InvalidLicenseExpression as e:\n    if 'Invalid license expression' in str(e) and not expr:\n        raise ValueError('project license is required') from e\n    raise","preventionTips":["Set a concrete SPDX license in pyproject.toml, e.g. license = \"MIT\"","Omit the license field rather than passing ''","Run `python -m build` locally to catch license errors before publishing"],"tags":["packaging","licenses","spdx","pep639","input-validation"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}