Comfy-Org / Comfy-Org/ComfyUI

LoadImage crashes on MPO / multi-picture JPEG since the pyav migration (#13594) — regression of #3422

Open
#14,127 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
133k
Forks
15.7k
Avg merge
1d 7h
Merged PRs (30d)
158

Description

### Summary
After #13594 made pyav the primary decoder in `LoadImage`, loading an **MPO / multi-picture JPEG** (a normal phone photo: a main image plus an embedded thumbnail of a *different* size) crashes:

```
RuntimeError: stack expects each tensor to be equal size, but got [H1, W1, 3] at entry 0 and [H2, W2, 3] at entry 1
```

This is a regression of #3416 / #3422, which added MPO handling back in 2024.

### Root cause
`nodes.py` `LoadImage.load_image` now calls `VideoFromFile(path).get_components()` unconditionally. pyav/libav decodes the MPO's embedded thumbnail as a **second, smaller video frame**, and `VideoFromFile.get_components_internal` does `torch.stack(frames)` (`comfy_api/latest/_input_impl/video_types.py:341`, and `:343` for the alpha channel) over ragged frames → crash.

The PIL `ImageSequence` fallback — which has a size guard and the `excluded_formats=['MPO']` handling added in #3422 — is **unreachable**, because it only runs when pyav returns 0 frames (`if components.images.shape[0] > 0`), and pyav always returns ≥1 frame.

`master` still has no ragged-frame guard in `get_components_internal`.

### Reproduce
```python
from PIL import Image
a = Image.new("RGB", (1360, 768), (200, 30, 30))
b = Image.new("RGB", (512, 289), (30, 30, 200)) # smaller embedded frame
a.save("mpo_test.jpg", format="MPO", append_images=[b], save_all=True)
# load mpo_test.jpg through the LoadImage node -> RuntimeError above
# (pyav decodes it as two differently-sized frames)
```

Any Ultra-HDR phone photo (Pixel, etc.) reproduces it too — same source as #3416.

### Suggested fix
Guard before the `torch.stack` in `get_components_internal`: real video frames share one resolution, so ragged frames indicate a still-image multi-picture container → collapse to the largest (main) frame, restoring the pre-#13594 first-frame behavior. (If an audio stream coexists with ragged frames, raising is reasonable, since a single-frame component cannot carry full-length audio coherently.)

### Related
- Regression of #3422 (2024 MPO handling).
- Sibling still-image regressions from #13594: #13619 (large-image `MemoryError`), #13791 (HEIC/HEIF silent crop).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.