{"record":{"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/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/licenses/__init__.py#L86-L122","documentation":"canonicalize_license_expression() validates and normalizes SPDX license expressions per PEP 639. The first check is for a falsy input (empty string or None). An empty license expression is invalid because PEP 639 requires a non-empty SPDX expression. This raises InvalidLicenseExpression (a ValueError subclass) at line 104.","triggerScenarios":"Calling canonicalize_license_expression(''), canonicalize_license_expression(None), or passing a whitespace-only string.","commonSituations":"Missing or empty license/license-expression field in pyproject.toml. Programmatic license validation where the value comes from user input or config that may be unset.","solutions":["Provide a valid SPDX license identifier (e.g. MIT, Apache-2.0)","Handle empty or None input before calling the function","Set a default license in your build configuration"],"exampleFix":"# before\nfrom packaging.licenses import canonicalize_license_expression\nnormalized = canonicalize_license_expression(project_license)  # empty string raises\n\n# after\nif not project_license:\n    raise ValueError(\"License expression is required\")\nnormalized = canonicalize_license_expression(project_license)","handlingStrategy":"validation","validationCode":"def safe_canonicalize_license(expr: str) -> str:\n    if not expr or not expr.strip():\n        raise ValueError(\"License expression must not be empty\")\n    from packaging.licenses import canonicalize_license_expression\n    return canonicalize_license_expression(expr)","typeGuard":"def is_non_empty_license(expr) -> bool:\n    return isinstance(expr, str) and bool(expr.strip())","tryCatchPattern":"from packaging.licenses import canonicalize_license_expression, InvalidLicenseExpression\n\ntry:\n    normalized = canonicalize_license_expression(license_expr)\nexcept InvalidLicenseExpression:\n    # prompt user for a valid SPDX license identifier\n    pass","preventionTips":["Require a license field in pyproject.toml","Validate non-empty before calling canonicalize","Use SPDX identifiers consistently (MIT, Apache-2.0, etc.)"],"tags":["packaging","spdx","pep639","license","validation","vendored"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}