{"record":{"id":"6220001e1d9456f9","repo":"Comfy-Org/ComfyUI","slug":"hash-check-failed","errorCode":"HASH_CHECK_FAILED","errorMessage":"Backend error while checking asset hash.","messagePattern":"Backend error while checking asset hash\\.","errorType":"http","errorClass":"UploadError","httpStatus":500,"severity":"error","filePath":"app/assets/api/upload.py","lineNumber":84,"sourceCode":"        fname = getattr(field, \"name\", \"\") or \"\"\n\n        if fname == \"hash\":\n            try:\n                s = ((await field.text()) or \"\").strip().lower()\n            except Exception:\n                raise UploadError(\n                    400, \"INVALID_HASH\", \"hash must be like 'blake3:<hex>'\"\n                )\n\n            if s:\n                provided_hash = normalize_and_validate_hash(s)\n                try:\n                    provided_hash_exists = check_hash_exists(provided_hash)\n                except Exception as e:\n                    logging.exception(\n                        \"check_hash_exists failed for hash=%s: %s\", provided_hash, e\n                    )\n                    raise UploadError(\n                        500,\n                        \"HASH_CHECK_FAILED\",\n                        \"Backend error while checking asset hash.\",\n                    )\n\n        elif fname == \"file\":\n            file_present = True\n            file_client_name = (field.filename or \"\").strip()\n\n            if provided_hash and provided_hash_exists is True:\n                # Hash exists - drain file but don't write to disk\n                try:\n                    while True:\n                        chunk = await field.read_chunk(8 * 1024 * 1024)\n                        if not chunk:\n                            break\n                        file_written += len(chunk)\n                except Exception:","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/app/assets/api/upload.py#L66-L102","documentation":"Raised from the HTTP >=400 branch (comfy_api_nodes/util/client.py:823) via _friendly_http_message when the error body is a JSON dict without a recognizable error.message structure — the entire body is serialized: 'API Error: {json.dumps(body)}'. This is the fallback for structured JSON error payloads the formatter does not specifically understand.","triggerScenarios":"Providers using nonstandard error schemas (e.g. {\"detail\": ...}, {\"errors\": [...]}, plain status objects) on a >=400 response; gateway-produced JSON error bodies; validation error arrays from framework defaults (FastAPI-style detail).","commonSituations":"Custom/newly integrated provider nodes whose error schema was never mapped; provider migrations changing error formats; upstream proxies injecting JSON errors.","solutions":["Parse the dumped JSON in the message to find the provider's actual error field (detail, errors, message elsewhere)","Fix the underlying cause indicated by that body","If integrating a new provider, normalize its error schema at the node boundary or extend _friendly_http_message to parse it","Use the logged response (status + body) as the source of truth"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def extract_provider_error(e_msg: str) -> dict | None:\n    if e_msg.startswith(\"API Error: \"):\n        try:\n            return json.loads(e_msg[len(\"API Error: \"):])\n        except json.JSONDecodeError:\n            return None\n    return None","tryCatchPattern":"try:\n    result = await sync_op(...)\nexcept Exception as e:\n    body = extract_provider_error(str(e))\n    reason = (body or {}).get(\"detail\") or (body or {}).get(\"errors\")\n    if reason:\n        handle(reason)\n    else:\n        raise","preventionTips":["Normalize the provider's error schema once at the integration boundary","Always cross-reference the logged full response body when the message is a raw JSON dump"],"tags":["http-4xx","json","provider","http"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}