Skip to content

GIF to video conversion: upload hangs forever when a GIF's frames x height exceeds the JPEG dimension limit #80259

Closed

Description

@adamsilverstein

Description

While testing the animated GIF → video conversion flow (#78410, made opt-in via block transforms in #80072), @bph discovered that some GIFs hang during the upload forever: the block spinner keeps spinning and the upload never completes and never errors. Other GIFs convert fine.

The failing sample is a 1517x855, 769-frame, 79-second screen recording GIF (2.2 MB). The working sample is a 600x385, 121-frame, 5-second GIF. Sample files to reproduce are attached in the first comment below.

Preliminary investigation

The mediabunny/WebCodecs video conversion itself is not the problem. Reproducing each pipeline stage in isolation in the browser (Chromium, mediabunny 1.49.0, wasm-vips 0.0.18):

Stage Working GIF (600x385, 121 frames) Failing GIF (1517x855, 769 frames)
convertGifToVideo() (mediabunny, all frames decoded + AVC encoded) OK, 0.3s OK, 2.0s, valid 2.2 MB MP4
vips poster conversion (convertImageFormat, GIF → JPEG) OK, 0.3s throws WebAssembly.Exception
vips poster conversion through the real @wordpress/worker-threads RPC (wrap/expose) OK, 0.3s promise never settles - hang reproduced
vips animated thumbnail resize (thumbnailBuffer 1024px, GIF out) fast OK but takes 44 seconds for a single sub-size

Root cause chain

  1. After a successful video conversion, transcodeGifItem queues a poster sideload that runs TranscodeImage (GIF → JPEG) through vips (private-actions.ts).
  2. convertImageFormat in @wordpress/vips loads any animation-capable input with [n=-1], i.e. all frames, regardless of the output format (packages/vips/src/index.ts). libvips represents this as one vertical strip: for the failing GIF that is a 1517 x 657,495 px image (855 x 769 frames).
  3. writeToBuffer('.jpeg') on that strip throws, because libjpeg's maximum image dimension is 65,500 px. wasm-vips surfaces this as a WebAssembly.Exception (not a JS Error).
  4. That exception never crosses the worker RPC boundary: the rejection is silently lost (a WebAssembly.Exception is not structured-cloneable and is not normalized to an Error before the reply is posted), and wrap() disables comctx's heartbeatCheck and registers no worker error handling (main-thread.ts, see also #79955, still open).
  5. transcodeImageItem's await vipsConvertImageFormat(...) therefore stays pending forever, the sideload never finishes, the parent item's finalize gate never closes, and the UI spins indefinitely.

Why one GIF works and the other doesn't: the JPEG strip height is frames x frame height. The working GIF is 121 x 385 = 46,585 px - just under the 65,500 px libjpeg limit. The failing GIF is 769 x 855 = 657,495 px. Any animated GIF whose frame count x height exceeds 65,500 px currently hangs the upload - which is easy to hit with screen recordings (this sample is only 2.2 MB).

Additional bugs found along the way

  • The poster is wrong even when it "works". The comment in transcodeGifItem says "vips decodes just the first GIF frame", but convertImageFormat loads all frames (n=-1), so the animated_video_poster JPEG produced for the working sample is the entire 600 x 46,585 px film strip (1.2 MB), not the first frame.
  • Animated thumbnail generation is extremely slow for long GIFs: ~44s per no-crop sub-size for the failing sample in wasm (single-threaded worker, sizes processed sequentially). Even without the hang, a user would stare at a spinner for minutes. Possibly worth a frame-count/pixel budget cutoff.

Proposed resolution

  1. Poster generation must load only the first frame. In convertImageFormat, only apply n=-1 when the output format also supports animation (GIF → JPEG poster then decodes one frame). This fixes the hang trigger, the film-strip poster bug, and makes poster generation fast.
  2. Never hang - fall back to a regular upload. When a GIF cannot be converted (or any companion step fails), the item should gracefully fall back to a plain server upload of the original GIF with no video transcoding, and the spinner must clear. Two layers:
    • Normalize non-Error throwables (e.g. WebAssembly.Exception) at the worker boundary so every failure reaches the main thread as a rejection.
    • Land #79955 (reject pending RPC calls on worker failure/termination) so a crashed worker can't strand the queue.
    • Audit transcodeGifItem/transcodeImageItem/sideload error paths so a failed companion never blocks the parent's finalize gate.
  3. E2E tests. A synthetic fixture GIF over the threshold (e.g. 100x100 px with ~700 frames keeps the file tiny while frames x height > 65,500) should cover:
    • Uploading + converting a "long" GIF completes without hanging and falls back to a regular GIF upload (or completes with a correct single-frame poster once fixed).
    • The generated poster is a single frame with the GIF's dimensions.
    • Regression coverage for the working case (small GIF converts, poster correct).

Step-by-step reproduction instructions

  1. On trunk, start the editor and insert an Image block.
  2. Upload the failing sample GIF (first comment) and opt into "Convert to video" (block transform / on-drop prompt).
  3. The spinner never stops; no error is shown. DevTools shows no console error either (the failure is swallowed inside the media worker).

Repeat with the working sample GIF: conversion completes, but inspect the animated_video_poster attachment - it is a film-strip JPEG rather than the first frame.

Activity

  1. added
    [Type] BugAn existing feature does not function as intended
    [Feature] MediaAnything that impacts the experience of managing media
    on Jul 14, 2026
  2. adamsilverstein commented on Jul 14, 2026

    @adamsilverstein
    MemberAuthor

    Sample GIF images

    Working image

    Image

    original in a zip: Tabs-block.gif.zip

    Failing image

    Image

    original in a zip: svgator-bird-alligator-cta.gif.zip

  3. moved this from 🏗️ In Progress to 📋 Iteration/Tracking Issues in WordPress 7.1 Editor Taskson Jul 14, 2026
  4. adamsilverstein commented on Jul 14, 2026

    @adamsilverstein
    MemberAuthor

    Working on this in #80260

  5. adamsilverstein commented on Jul 15, 2026

    @adamsilverstein
    MemberAuthor
  6. moved this from 📋 Iteration/Tracking Issues to 🏗️ In Progress in WordPress 7.1 Editor Taskson Jul 15, 2026
  7. moved this from 🏗️ In Progress to ✅ Done in WordPress 7.1 Editor Taskson Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

[Feature] Client Side MediaMedia processing in the browser with WASM[Status] In ProgressTracking issues with work in progress[Type] BugAn existing feature does not function as intended

Type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions