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

  1. Re-download or regenerate the .ply and confirm it opens in a PLY viewer.
  2. 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).
  3. Check the file size against the source; a few-hundred-byte file is a stub or error page.
  4. 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

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


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/9638f0986b3f61f3. Report an issue: GitHub.