{"record":{"id":"5ad80565421f94fb","repo":"Comfy-Org/ComfyUI","slug":"invalid-hash-5ad805","errorCode":"INVALID_HASH","errorMessage":"hash must be 'blake3:<hex>'","messagePattern":"hash must be 'blake3:<hex>'","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"app/assets/helpers.py","lineNumber":57,"sourceCode":"\n\ndef normalize_tags(tags: list[str] | None) -> list[str]:\n    \"\"\"\n    Normalize a list of tags by:\n      - Stripping whitespace.\n      - Removing exact duplicates while preserving order and case.\n    \"\"\"\n    return list(dict.fromkeys(t.strip() for t in (tags or []) if (t or \"\").strip()))\n\n\ndef validate_blake3_hash(s: str) -> str:\n    \"\"\"Validate and normalize a blake3 hash string.\n\n    Returns canonical 'blake3:<hex>' or raises ValueError.\n    \"\"\"\n    s = s.strip().lower()\n    if not s or \":\" not in s:\n        raise ValueError(\"hash must be 'blake3:<hex>'\")\n    algo, digest = s.split(\":\", 1)\n    if (\n        algo != \"blake3\"\n        or len(digest) != 64\n        or any(c for c in digest if c not in \"0123456789abcdef\")\n    ):\n        raise ValueError(\"hash must be 'blake3:<hex>'\")\n    return f\"{algo}:{digest}\"\n","sourceCodeStart":39,"sourceCodeEnd":66,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/app/assets/helpers.py#L39-L66","documentation":"First failure mode of validate_blake3_hash in app/assets/helpers.py: the input (after strip+lower) is empty or contains no ':' separator, so it cannot be an 'algo:digest' string. The function's contract is to return a canonical 'blake3:<64-hex>' string or raise ValueError.","triggerScenarios":"Passing a bare 64-character hex digest with no 'blake3:' prefix, an empty/whitespace string, or any string without a colon — e.g. ingest APIs receiving the raw hasher output instead of the prefixed form.","commonSituations":"Clients that compute blake3 locally and send just the hex digest; config files or JSON payloads storing the hash without the algorithm prefix; copy-pasting only the digest half of a stored hash.","solutions":["Prefix the digest: send 'blake3:<hex>' with exactly 64 lowercase hex characters.","If you hold a bare digest, construct the canonical form before calling: f\"blake3:{digest}\".","Validate the shape client-side with a regex before submitting.","Map the ValueError to 400 INVALID_HASH at the API boundary."],"exampleFix":"// before\nvalidate_blake3_hash(digest_hex)  # e.g. \"af12...\" no prefix\n\n// after\nvalidate_blake3_hash(f\"blake3:{digest_hex}\")","handlingStrategy":"validation","validationCode":"import re\n\ndef looks_like_blake3(s: str) -> bool:\n    return re.fullmatch(r\"\\s*blake3:[0-9a-fA-F]{64}\\s*\", s or \"\") is not None","typeGuard":null,"tryCatchPattern":"try:\n    canonical = validate_blake3_hash(raw)\nexcept ValueError as e:\n    raise HTTPException(status_code=400, detail={\"code\": \"INVALID_HASH\", \"message\": str(e)})","preventionTips":["Always store and send hashes as 'blake3:<64 hex>'; wrap bare digests with f\"blake3:{digest}\".","Validate the shape with a regex before submission.","Remember the function lowercases input, so case alone never fails."],"tags":["hashing","validation","assets","bad-request"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}