{"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":178,"sourceCode":"\n            normalized_tokens.append(EXCEPTIONS[token][\"id\"])\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                if not license_ref_allowed.match(final_token):\n                    message = f\"Invalid licenseref: {final_token!r}\"\n                    raise InvalidLicenseExpression(message)\n                normalized_tokens.append(license_refs[final_token] + suffix)\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\n    normalized_expression = \" \".join(normalized_tokens)\n\n    return cast(\n        \"NormalizedLicenseExpression\",\n        normalized_expression.replace(\"( \", \"(\").replace(\" )\", \")\"),\n    )\n","sourceCodeStart":160,"sourceCodeEnd":187,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/licenses/__init__.py#L160-L187","documentation":"Thrown by canonicalize_license_expression in packaging.licenses when a token in the SPDX expression does not match any identifier in the LICENSES table (nor a 'licenseref-' prefix). The library validates against the canonical SPDX license list, so any non-SPDX or misspelled identifier is rejected. It is raised as InvalidLicenseExpression after the expression's boolean structure has already been validated.","triggerScenarios":"Calling canonicalize_license_expression('MIT OR BSD-3') (BSD-3 is not SPDX; the correct id is 'BSD-3-Clause'), or canonicalize_license_expression('Apache2') (correct is 'Apache-2.0'), or passing a custom/proprietary license id without the 'licenseref-' prefix. Also triggered by typos like 'GPL-v3' instead of 'GPL-3.0-only'.","commonSituations":"License classifiers copied from a README that uses informal names; project metadata generated by tooling that does not enforce SPDX; upgrading to a packaging version that added license normalization (newer metadata 2.4+ License-Expression field). Mirrors the 'Unknown license exception' sibling raised at line 158 for the RHS of WITH.","solutions":["Replace the offending token with its canonical SPDX identifier (look it up at https://spdx.org/licenses/).","For a non-SPDX license, prefix the token with 'licenseref-' (e.g. 'licenseref-mycorp-1.0') and ensure it matches the licenseref_allowed regex.","If the expression was user-supplied, catch InvalidLicenseExpression and report the unknown token back to the user rather than crashing.","Validate the expression with a license lint tool (e.g. spdx-tools) before passing it to packaging."],"exampleFix":"// before\ncanonicalize_license_expression(\"MIT OR BSD-3\")\n// after\ncanonicalize_license_expression(\"MIT OR BSD-3-Clause\")","handlingStrategy":"validation","validationCode":"from packaging.licenses import _LICENSES as LICENSES  # or canonicalize in try/except\nimport re\nlicense_ref_re = re.compile(r'^licenseref-.+$')\n\ndef is_valid_license_token(tok: str) -> bool:\n    if tok.endswith('+'):\n        tok = tok[:-1]\n    if tok.startswith('licenseref-'):\n        return bool(license_ref_re.match(tok))\n    return tok in LICENSES","typeGuard":null,"tryCatchPattern":"from packaging.licenses import InvalidLicenseExpression\ntry:\n    canon = canonicalize_license_expression(expr)\nexcept InvalidLicenseExpression as e:\n    # report unknown token to user, keep input unchanged\n    log.warning('bad license expression %r: %s', expr, e)","preventionTips":["Use a license lint tool (spdx-tools) before persisting license strings.","Source identifiers from https://spdx.org/licenses/ IDs only.","For proprietary licenses use 'licenseref-<name>' form.","Catch InvalidLicenseExpression on user input and surface a helpful message."],"tags":["licenses","spdx","metadata","validation"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}