{"record":{"id":"7db903a5760f592e","repo":"pypa/pip","slug":"unknown-license-final-token-r","errorCode":null,"errorMessage":"Unknown license: {final_token!r}","messagePattern":"Unknown license: (.+?)","errorType":"validation","errorClass":"InvalidLicenseExpression","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/licenses/__init__.py","lineNumber":189,"sourceCode":"            last_license_start = False\n        else:\n            if token.endswith(\"+\"):\n                final_token = token[:-1]\n                suffix = \"+\"\n            else:\n                final_token = token\n                suffix = \"\"\n\n            if final_token.startswith(\"licenseref-\"):\n                license_ref_id = final_token[len(\"licenseref-\") :]\n                if suffix or not license_ref_allowed.match(license_ref_id):\n                    message = f\"Invalid licenseref: {token!r}\"\n                    raise InvalidLicenseExpression(message)\n                normalized_tokens.append(license_refs[final_token])\n            else:\n                if final_token not in LICENSES:\n                    message = f\"Unknown license: {final_token!r}\"\n                    raise InvalidLicenseExpression(message)\n                normalized_tokens.append(LICENSES[final_token][\"id\"] + suffix)\n            last_license_start = True\n\n    normalized_expression = \" \".join(normalized_tokens)\n\n    return cast(\n        \"NormalizedLicenseExpression\",\n        normalized_expression.replace(\"( \", \"(\").replace(\" )\", \")\"),\n    )\n","sourceCodeStart":171,"sourceCodeEnd":199,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/licenses/__init__.py#L171-L199","documentation":"Raised during the final pass of canonicalize_license_expression (licenses/__init__.py:187-189). Any token that is not a boolean op, paren, WITH, or a LicenseRef-* must be a recognized SPDX license identifier present in the LICENSES table; otherwise InvalidLicenseExpression is raised. The token is lowercased before lookup, so only the exact SPDX spelling (case-insensitive) is accepted.","triggerScenarios":"Passing 'Use-it-after-midnight', 'proprietary', 'BSD' (too vague — use 'BSD-3-Clause'), 'GPLv3' (use 'GPL-3.0-only'/'GPL-3.0-or-later'), or 'Apache' (use 'Apache-2.0').","commonSituations":"Using colloquial or display license names; forgetting the -only/-or-later suffix on GPL family licenses; a newer SPDX id absent from an older vendored packaging; custom licenses not written as LicenseRef-*.","solutions":["Use the exact SPDX license identifier (e.g. 'MIT', 'Apache-2.0', 'BSD-3-Clause', 'GPL-3.0-or-later').","For non-SPDX licenses, use a 'LicenseRef-*' token instead."],"exampleFix":"# before\ncanonicalize_license_expression('GPLv3')\n# after\ncanonicalize_license_expression('GPL-3.0-or-later')","handlingStrategy":"try-catch","validationCode":"from pip._vendor.packaging.licenses._spdx import LICENSES\n\ndef is_known_license(token: str) -> bool:\n    t = token.lower().removesuffix(\"+\")\n    return t.startswith(\"licenseref-\") or t in LICENSES\n","typeGuard":null,"tryCatchPattern":"from packaging.licenses import canonicalize_license_expression, InvalidLicenseExpression\n\ntry:\n    canonicalize_license_expression(expr)\nexcept InvalidLicenseExpression as e:\n    ...\n","preventionTips":["Use canonical SPDX license ids (MIT, Apache-2.0, BSD-3-Clause, GPL-3.0-or-later).","Use LicenseRef-* for non-SPDX licenses."],"tags":["spdx","license","validation","packaging"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}