{"record":{"id":"3e839ec1ef446130","repo":"pypa/pip","slug":"at-least-one-hash-must-be-provided","errorCode":null,"errorMessage":"At least one hash must be provided","messagePattern":"At least one hash must be provided","errorType":"validation","errorClass":"PylockValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/pylock.py","lineNumber":273,"sourceCode":"    if \"/\" in path:\n        return path.rsplit(\"/\", 1)[-1]\n    elif \"\\\\\" in path:\n        return path.rsplit(\"\\\\\", 1)[-1]\n    else:\n        return path\n\n\ndef _url_name(url: str | None) -> str | None:\n    if not url:\n        return None\n    url_path = urlparse(url).path\n    # The last path component is percent-encoded, so decode it to the file name\n    return unquote(url_path.rsplit(\"/\", 1)[-1])\n\n\ndef _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]:\n    if not hashes:\n        raise PylockValidationError(\"At least one hash must be provided\")\n    if not all(isinstance(hash_val, str) for hash_val in hashes.values()):\n        raise PylockValidationError(\"Hash values must be strings\")\n    return hashes\n\n\nclass PylockValidationError(Exception):\n    \"\"\"Raised when when input data is not spec-compliant.\"\"\"\n\n    context: str | None = None\n    message: str\n\n    def __init__(\n        self,\n        cause: str | Exception,\n        *,\n        context: str | None = None,\n    ) -> None:\n        if isinstance(cause, PylockValidationError):","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/pylock.py#L255-L291","documentation":"Raised by _validate_hashes in pylock.py:271-273, which is applied to the 'hashes' table of every PackageSdist, PackageWheel and PackageArchive. The hashes mapping must contain at least one algorithm->digest entry; an empty (or effectively empty) hashes table raises PylockValidationError. (A completely missing hashes key is a separate 'Missing required value' error from _get_required_as.)","triggerScenarios":"In pylock TOML: [[packages.sdist]] with hashes = {}, or a wheel/archive entry whose hashes table is empty.","commonSituations":"Generating a lock file before computing integrity hashes; using an empty placeholder hashes table during development.","solutions":["Populate at least one hash entry, conventionally sha256, e.g. hashes = { sha256 = \"<hex>\" }."],"exampleFix":"# before\n[[packages.sdist]]\nname = \"x-1.0.tar.gz\"\nhashes = {}\n# after\n[[packages.sdist]]\nname = \"x-1.0.tar.gz\"\nhashes = { sha256 = \"abcdef...\" }","handlingStrategy":"validation","validationCode":"def has_at_least_one_hash(hashes) -> bool:\n    return bool(hashes) and len(hashes) >= 1\n","typeGuard":null,"tryCatchPattern":"from packaging.pylock import Pylock, PylockValidationError\n\ntry:\n    Pylock.from_dict(d)\nexcept PylockValidationError as e:\n    ...\n","preventionTips":["Compute and record at least one digest (sha256) for every artifact before emitting the lock file.","Treat an empty hashes table as a build error, not a placeholder."],"tags":["pylock","validation","hashes"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}