{"record":{"id":"99c5d6b11c9d1a5a","repo":"Comfy-Org/ComfyUI","slug":"file3dtosplat-ksplat-has-no-splats","errorCode":null,"errorMessage":"File3DToSplat: .ksplat has no splats","messagePattern":"File3DToSplat: \\.ksplat has no splats","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_extras/nodes_gaussian_splat.py","lineNumber":333,"sourceCode":"            rot = _norm_quat(rec['rot'].astype(np.float32))                       # wxyz\n            scale = rec['scale'].astype(np.float32)\n            if level == 0:\n                xyz = rec['center'].astype(np.float32)\n            else:\n                buckets = np.frombuffer(data, '<f4', count=bucket_count * 3, offset=base + meta_bytes).reshape(-1, 3)\n                idx = np.empty(cnt, np.int64)\n                full_splats = full_buckets * bucket_size\n                nf = min(full_splats, cnt)\n                idx[:nf] = np.arange(nf) // bucket_size\n                if cnt > full_splats:\n                    lengths = np.frombuffer(data, '<u4', count=partial_buckets, offset=base)\n                    idx[full_splats:] = np.repeat(full_buckets + np.arange(partial_buckets), lengths)[:cnt - full_splats]\n                xyz = (rec['center'].astype(np.float32) - scale_range) * (block_size / 2.0 / scale_range) + buckets[idx]\n            parts.append((xyz, scale, rot, colf[:, 3].copy(), _rgb_to_sh_dc(colf[:, :3])))\n        base += bytes_per_splat * sec_max + buckets_store\n\n    if not parts:\n        raise ValueError(\"File3DToSplat: .ksplat has no splats\")\n    return tuple(np.concatenate([p[i] for p in parts]) for i in range(5))\n\n\ndef _parse_spz_gaussian(data: bytes):\n    # Niantic .spz (gzip-wrapped), versions 1-3. Base color only (SH skipped). See spark's SpzReader.\n    raw = gzip.decompress(data)\n    if struct.unpack_from('<I', raw, 0)[0] != _SPZ_MAGIC:\n        raise ValueError(\"File3DToSplat: invalid .spz (bad magic)\")\n    version = struct.unpack_from('<I', raw, 4)[0]\n    n = struct.unpack_from('<I', raw, 8)[0]\n    frac_bits = raw[13]\n    off = 16\n\n    if version == 1:\n        xyz = np.frombuffer(raw, '<f2', count=n * 3, offset=off).astype(np.float32).reshape(n, 3)\n        off += n * 6\n    elif version in (2, 3):\n        b = np.frombuffer(raw, np.uint8, count=n * 9, offset=off).reshape(n, 3, 3).astype(np.int64)","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_extras/nodes_gaussian_splat.py#L315-L351","documentation":"Raised by _parse_ksplat_gaussian when after iterating all declared sections no section contributed any splats (parts list is empty). Every section either had a splat count of 0 or was skipped, so the file describes a valid header but contains zero gaussians. The parser refuses because concatenating zero parts would yield degenerate empty tensors.","triggerScenarios":"File3DToSplat on a .ksplat whose header is well-formed but whose maxSections sections all report cnt == 0: an empty export, a header-only stub, or a file whose section data was stripped.","commonSituations":"Exporting an empty scene from a splat viewer; truncated file that kept the 4096-byte header but lost section payloads; testing with placeholder ksplat files.","solutions":["Re-export the scene from the source tool with actual gaussians present.","Verify the file size is well beyond 4096 + sections*1024 bytes (an empty-header file is suspiciously small).","Load the file in the viewer that produced it to confirm it shows splats."],"exampleFix":"null","handlingStrategy":"validation","validationCode":"import struct\ndata = open(path, 'rb').read()\nmax_sections = struct.unpack_from('<I', data, 4)[0]\ntotal = sum(struct.unpack_from('<I', data, 4096 + s * 1024)[0] for s in range(max_sections))\nif total == 0:\n    raise ValueError('ksplat contains zero splats; re-export the scene')","typeGuard":null,"tryCatchPattern":"try:\n    result = _parse_ksplat_gaussian(data)\nexcept ValueError as e:\n    if 'no splats' in str(e):\n        re_export_nonempty_scene(path)\n    else:\n        raise","preventionTips":["Sanity-check that exported ksplat files have a non-zero splat count.","Reject suspiciously small ksplat files (header-only) at load time."],"tags":["gaussian-splatting","ksplat","empty-input","file-format"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}