{"record":{"id":"8449889bc254e6cc","repo":"Comfy-Org/ComfyUI","slug":"file3dtosplat-invalid-ksplat-compression-level","errorCode":null,"errorMessage":"File3DToSplat: invalid .ksplat compression level {level}","messagePattern":"File3DToSplat: invalid \\.ksplat compression level (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_extras/nodes_gaussian_splat.py","lineNumber":286,"sourceCode":"    if len(data) % 32 != 0:\n        raise ValueError(\"File3DToSplat: .splat size is not a multiple of 32 bytes\")\n    rec = np.frombuffer(data, np.dtype([('xyz', '<f4', 3), ('scale', '<f4', 3),\n                                        ('rgba', 'u1', 4), ('quat', 'u1', 4)]))\n    rgba = rec['rgba'].astype(np.float32) / 255.0\n    rot = _norm_quat((rec['quat'].astype(np.float32) - 128.0) / 128.0)            # wxyz\n    return (rec['xyz'].astype(np.float32), rec['scale'].astype(np.float32), rot,\n            rgba[:, 3].copy(), _rgb_to_sh_dc(rgba[:, :3]))\n\n\ndef _parse_ksplat_gaussian(data: bytes):\n    # mkkellogg SplatBuffer: 4096-byte header, N section headers, then per-section splat data. Supports\n    # levels 0 (float) / 1 (half + bucketed positions) / 2 (half, uint8 SH). SH is skipped (base color kept).\n    if data[0] != 0:\n        raise ValueError(f\"File3DToSplat: unsupported .ksplat version {data[0]}.{data[1]}\")\n    max_sections = struct.unpack_from('<I', data, 4)[0]\n    level = struct.unpack_from('<H', data, 20)[0]\n    if level not in _KSPLAT_COMPRESSION:\n        raise ValueError(f\"File3DToSplat: invalid .ksplat compression level {level}\")\n    bc, bs, br, bcol, bshc, default_range = _KSPLAT_COMPRESSION[level]\n\n    parts = []\n    base = 4096 + max_sections * 1024\n    for s in range(max_sections):\n        so = 4096 + s * 1024\n        cnt = struct.unpack_from('<I', data, so + 0)[0]\n        sec_max = struct.unpack_from('<I', data, so + 4)[0]\n        bucket_size = struct.unpack_from('<I', data, so + 8)[0]\n        bucket_count = struct.unpack_from('<I', data, so + 12)[0]\n        block_size = struct.unpack_from('<f', data, so + 16)[0]\n        bucket_store = struct.unpack_from('<H', data, so + 20)[0]\n        scale_range = struct.unpack_from('<I', data, so + 24)[0] or default_range\n        full_buckets = struct.unpack_from('<I', data, so + 32)[0]\n        partial_buckets = struct.unpack_from('<I', data, so + 36)[0]\n        sh_components = _KSPLAT_SH_COMPONENTS.get(struct.unpack_from('<H', data, so + 40)[0], 0)\n        bytes_per_splat = bc + bs + br + bcol + sh_components * bshc\n        meta_bytes = partial_buckets * 4","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_extras/nodes_gaussian_splat.py#L268-L304","documentation":"Raised by _parse_ksplat_gaussian when the compression-level field at header offset 20 is not one of the supported levels keyed in _KSPLAT_COMPRESSION (levels 0, 1, 2). The level determines the per-splat record layout (float vs half precision, bucketed positions, uint8 SH), so an unknown level cannot be decoded.","triggerScenarios":"File3DToSplat on a .ksplat that reports a compression level outside 0-2 — written by a newer/other variant of the SplatBuffer writer, or corrupted header bytes; also possible when a non-ksplat binary misdetects as ksplat and its bytes at offset 20 land on an unsupported value.","commonSituations":"Newer mkkellogg library adding level 3+; byte-level corruption of the 4096-byte header; wrong file fed to the node.","solutions":["Re-export the asset as .ply or .spz, which are fully supported.","Regenerate the .ksplat with a library version writing level 0-2 (level 0 is what SplatToFile3D itself writes).","If the file is valuable, verify its integrity against the source."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"import struct\ndata = open(path, 'rb').read()\nlevel = struct.unpack_from('<H', data, 20)[0]\nif level not in (0, 1, 2):\n    raise ValueError(f'ksplat compression level {level} unsupported (need 0-2)')","typeGuard":"def ksplat_level_supported(data: bytes) -> bool:\n    return len(data) >= 22 and struct.unpack_from('<H', data, 20)[0] in (0, 1, 2)","tryCatchPattern":"try:\n    result = _parse_ksplat_gaussian(data)\nexcept ValueError as e:\n    if 'compression level' in str(e):\n        re_export_as_ply(path)\n    else:\n        raise","preventionTips":["Export ksplat at level 0 from SplatToFile3D or a matching library version for round-trip safety.","Use .ply for long-term storage of splat assets."],"tags":["gaussian-splatting","ksplat","unsupported-feature","file-format"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}