{"record":{"id":"ad2e366a6567d0c2","repo":"Comfy-Org/ComfyUI","slug":"file3dtosplat-splat-size-is-not-a-multiple-of-32","errorCode":null,"errorMessage":"File3DToSplat: .splat size is not a multiple of 32 bytes","messagePattern":"File3DToSplat: \\.splat size is not a multiple of 32 bytes","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_extras/nodes_gaussian_splat.py","lineNumber":269,"sourceCode":"        rest = sorted((k for k in names if k.startswith('f_rest_')), key=lambda s: int(s.split('_')[-1]))\n        if rest:\n            r = np.stack([c(k) for k in rest], 1)                                 # (N, 3*(K-1)) channel-major\n            kk = r.shape[1] // 3 + 1\n            r = r.reshape(n, 3, kk - 1).transpose(0, 2, 1)                        # -> (N, K-1, 3)\n            sh = np.concatenate([dc[:, None, :], r], 1)\n        else:\n            sh = dc[:, None, :]\n    elif 'red' in names:\n        sh = _rgb_to_sh_dc(np.stack([c('red'), c('green'), c('blue')], 1) / 255.0)\n    else:\n        sh = np.zeros((n, 1, 3), np.float32)\n    return xyz, scale, rot, opacity, sh\n\n\ndef _parse_splat_gaussian(data: bytes):\n    # antimatter15 .splat: 32-byte records (f32 xyz, f32 scale, u8 rgba, u8 quat as (b-128)/128 wxyz).\n    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]","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_extras/nodes_gaussian_splat.py#L251-L287","documentation":"Raised by _parse_splat_gaussian when parsing antimatter15-format .splat data whose byte length is not a multiple of 32. Each splat is a fixed 32-byte record (3 f32 position + 3 f32 scale + 4 u8 RGBA + 4 u8 quantized quaternion), so any remainder indicates a truncated, corrupted, or non-.splat file.","triggerScenarios":"File3DToSplat on data that failed the ply/spz/ksplat signature checks but whose length happens not to divide by 32 — typically a genuinely truncated or corrupted .splat, or an arbitrary binary file with an unlucky length. Also a partially uploaded/downloaded .splat.","commonSituations":"Truncated download of a .splat; file cut off mid-write; feeding a random binary blob to the node hoping it is a splat.","solutions":["Verify the file size: size % 32 must be 0 and match the expected splat count times 32.","Re-download or regenerate the .splat from its source.","Confirm the file is actually antimatter15 .splat format, not another splat flavor renamed to .splat."],"exampleFix":"null","handlingStrategy":"validation","validationCode":"data = open(path, 'rb').read()\nif data[:3] == b'ply' or data[:2] == b'\\x1f\\x8b' or (len(data) >= 2 and data[0] == 0 and data[1] >= 1):\n    pass  # other formats\nelif len(data) % 32 != 0:\n    raise ValueError(f'{path} is not a valid 32-byte-record .splat (size {len(data)})')","typeGuard":"def is_splat_v1(data: bytes) -> bool:\n    return (len(data) > 0 and len(data) % 32 == 0\n            and data[:3] != b'ply' and data[:2] != b'\\x1f\\x8b')","tryCatchPattern":"try:\n    result = _parse_splat_gaussian(data)\nexcept ValueError as e:\n    if 'multiple of 32 bytes' in str(e):\n        re_fetch_or_reconvert(path)\n    else:\n        raise","preventionTips":["Check size % 32 == 0 before treating a file as antimatter15 .splat.","Verify downloads by size against the source."],"tags":["gaussian-splatting","splat-format","corrupt-file","validation"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}