Comfy-Org/ComfyUI · error · ValueError
File3DToSplat: not a PLY (missing end_header)
Error message
File3DToSplat: not a PLY (missing end_header)
What it means
Raised by _parse_ply_gaussian when the input bytes selected as PLY (magic b'ply') contain no b'end_header' token anywhere. The parser splits the file at end_header to separate the ASCII header from the binary body; without it the file is not a complete PLY. This usually means the file is truncated, corrupted, or not actually a PLY despite its magic bytes.
Source
Thrown at comfy_extras/nodes_gaussian_splat.py:214
'float': 'f4', 'double': 'f8', 'int8': 'i1', 'uint8': 'u1', 'int16': 'i2', 'uint16': 'u2',
'int32': 'i4', 'uint32': 'u4', 'float32': 'f4', 'float64': 'f8'}
_KSPLAT_COMPRESSION = { # level -> (bytesPerCenter, scale, rotation, color, shComponent, defaultScaleRange)
0: (12, 12, 16, 4, 4, 1), 1: (6, 6, 8, 4, 2, 32767), 2: (6, 6, 8, 4, 1, 32767)}
_KSPLAT_SH_COMPONENTS = {0: 0, 1: 9, 2: 24, 3: 45}
def _rgb_to_sh_dc(rgb):
return ((np.asarray(rgb, np.float32) - 0.5) / _C0)[:, None, :] # (N,3) base color -> (N,1,3) SH DC
def _norm_quat(q):
return q / np.linalg.norm(q, axis=1, keepdims=True).clip(1e-12)
def _parse_ply_gaussian(data: bytes):
end = data.find(b'end_header')
if end < 0:
raise ValueError("File3DToSplat: not a PLY (missing end_header)")
header = data[:end].decode('ascii', 'replace')
body = end + len(b'end_header')
body += 2 if data[body:body + 2] == b'\r\n' else 1
count, props, in_vertex = 0, [], False
for line in header.splitlines():
p = line.split()
if not p:
continue
if p[0] == 'format' and p[1] != 'binary_little_endian':
raise ValueError(f"File3DToSplat: unsupported PLY format '{p[1]}' (need binary_little_endian)")
if p[0] == 'element':
in_vertex = p[1] == 'vertex'
if in_vertex:
count = int(p[2])
elif p[0] == 'property' and in_vertex:
if p[1] == 'list':
raise ValueError("File3DToSplat: PLY vertex has list properties (unsupported)")
props.append((p[2], '<' + _PLY_DTYPES[p[1]]))View on GitHub (pinned to 1c6d8d45b3)
Solutions
- Re-download or regenerate the .ply and confirm it opens in a PLY viewer.
- Verify the file starts with a full 'ply\nformat binary_little_endian 1.0\n...\nend_header\n' header (inspect with a hex/text editor).
- Check the file size against the source; a few-hundred-byte file is a stub or error page.
- Ensure no process is still writing the file when ComfyUI reads it.
Example fix
null
Defensive patterns
Strategy: validation
Validate before calling
data = open(path, 'rb').read()
if data[:3] != b'ply' or b'end_header' not in data[:65536]:
raise ValueError(f"{path} is not a complete binary PLY") Type guard
def is_complete_ply(data: bytes) -> bool:
return data[:3] == b'ply' and b'end_header' in data Try / catch
try:
result = _parse_ply_gaussian(data)
except ValueError as e:
if 'missing end_header' in str(e):
re_download_or_regenerate(path)
else:
raise Prevention
- Verify downloads completed (size/mtime) before loading.
- Sniff for both the 'ply' magic and an end_header line before parsing.
- Never read a file another process is still writing.
When it happens
Trigger: File3DToSplat loads a file whose first three bytes are 'ply' (so _detect_splat_format routes it to the PLY parser) but the header is cut off before end_header: truncated download, file saved mid-write, a text/HTML error page that happens to start with 'ply', or a header-only file.
Common situations: Interrupted or partial download of a .ply; a .ply written by another process still being written when read; wrong file passed to the node (renamed extension).
Related errors
- File3DToSplat: unsupported PLY format '{p[1]}' (need binary_
- File3DToSplat: PLY vertex has list properties (unsupported)
- File3DToSplat: invalid .spz (bad magic)
- File3DToSplat: .splat size is not a multiple of 32 bytes
- File3DToSplat: unsupported .ksplat version {data[0]}.{data[1
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/9638f0986b3f61f3.
Report an issue: GitHub.