Skip to content

HEIC upload hangs indefinitely instead of falling back to server-side processing #81043

Closed

Description

@369work

Description

Uploading a HEIC file in the block editor causes the upload spinner to spin indefinitely and never complete. The original .heic file is saved to the uploads folder, but no resized sub-sizes (150/300/768/1024) are generated. No error appears in the browser console.

Step-by-step reproduction instructions

Steps to Reproduce

  1. Open the block editor and add an Image block (or use the Media Library uploader).
  2. Upload a .heic file.
  3. Observe the upload spinner.

Screenshots, screen recording, code snippet

Image Image

Environment info

Environment

  • WordPress: 7.1-beta4
  • Subdirectory: No
  • PHP: 8.4.23
  • Server: PHP/8.4.23 (Development Server)
  • Database: WP_SQLite_Driver (Server: 8.0.38 / Client: 3.53.2)
  • Browser: Edge 150.0.0.0
  • OS: Windows 11
  • Theme: Twenty Twenty-Three 1.6
  • MU Plugins:
    • 99-studio-loader.php
  • Plugins:
    • Test Reports 1.3.0
    • WordPress Beta Tester 4.0.0
  • Note: Windows HEVC Video Extension (Microsoft Store) is not installed.

Expected Results

Per the "Client-Side Media Processing in WordPress 7.1" dev note, HEIC decoding depends on platform codecs and requires HEVC support on Windows. When the browser/OS cannot process the file client-side, WordPress should fall back to server-side processing automatically and transparently, with no visible change to the user.

Actual Results

The upload spinner keeps spinning and never completes. The original .heic file is written to the uploads folder, but no resized sub-sizes are generated. No error is shown in the browser console.

Additional Notes

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Activity

  1. added
    [Type] BugAn existing feature does not function as intended
    on Jul 31, 2026
  2. singhakanshu00 commented on Aug 3, 2026

    @singhakanshu00
    Contributor

    Root cause found — silent promise hang in strategy 2 of the HEIC client-side decoder

    I traced this down to canvasConvertToJpeg() in packages/upload-media/src/canvas-utils.ts, which tries three decoding strategies in order:

    1. reateImageBitmap() + OffscreenCanvas
    2. WebCodecs ImageDecoder API
    3. HEIC container parsing + WebCodecs VideoDecoder

    Strategies 1 and 3 are both wrapped in try/catch, so a decode failure falls through cleanly to the next strategy. Strategy 2 was not (canvas-utils.ts:70-100 before this fix).

    The failure mode: ImageDecoder.isTypeSupported(file.type) can optimistically return true based on container-format recognition alone, without confirming the platform actually has the underlying HEVC codec (exactly the Windows-without-HEVC-extension scenario in this report). When that mismatch happens, decoder.decode() doesn't reject — it just never settles. Since nothing throws, the try/finally around it never resolves either, the await canvasConvertToJpeg(...) call in private-actions.ts:886 (inside prepareItem()) hangs forever, and dispatch.cancelItem(...) is never reached. That matches the report exactly: spinner never resolves, original file already saved (it's written before this code path even queues the Upload operation), no console error (nothing ever throws or rejects).

    Proposed fix

    In canvas-utils.ts:

    • Race decoder.decode() against an 8s timeout (IMAGE_DECODER_TIMEOUT).
    • On timeout, force-close the decoder — per the WebCodecs spec, closing a decoder rejects any of its pending decode() calls, which gives us a real rejection to catch instead of a promise that never settles.
    • Wrap strategy 2 in try/catch (matching strategies 1 and 3) so a timeout or rejection falls through to strategy 3 instead of hanging the whole chain.

    Net effect: on a platform where the codec claim is wrong, we now fail over to strategy 3 (or the final "browser can't decode HEIC" error) within ~8s instead of hanging indefinitely.

    Open question before I put up a PR: is 8s a reasonable timeout value here, or is there a preferred constant/convention elsewhere in the codebase for this kind of guard?

    Happy to open the PR once there's agreement on the approach.

  3. t-hamano commented on Aug 4, 2026

    @t-hamano
    Contributor
  4. adamsilverstein commented on Aug 4, 2026

    @adamsilverstein
    Member

    Open question before I put up a PR: is 8s a reasonable timeout value here, or is there a preferred constant/convention elsewhere in the codebase for this kind of guard?

    @singhakanshu00 thanks for the analysis. 8s feels like too much time to me if anything. I don't think we have an existing convention.

    Happy to open the PR once there's agreement on the approach.

    Yes, please work on a PR for this and we can ask @369work to test your fix.

    @369work thanks so much for opening this bug report and testing this on your system. Some follow up questions:

    • I assume your server does not support HEIC, otherwise it would have used that path. Can you confirm by checking Tools->Site Health->Info->Media?
    • Can you try installing the Windows HEVC Video Extension to confirm it fixes the issue for you. Also, how long does the upload take roughly (thing might help inform the timeout decision)
  5. moved this from 📥 Todo to 🏗️ In Progress in WordPress 7.1 Editor Taskson Aug 4, 2026
  6. 369work commented on Aug 5, 2026

    @369work
    ContributorAuthor

    Thanks for the follow-up questions, @adamsilverstein!

    Site Health check:
    Active editor is WP_Image_Editor_GD. GD's supported file formats are GIF, JPEG, PNG, WebP, BMP, AVIF, XPM — HEIC/HEIF is not listed. So the server-side editor does not support HEIC, confirming your assumption. Screenshot attached below.

    HEVC Video Extension:
    I checked the Microsoft Store, and only the paid version of the "HEVC Video Extensions from Device Manufacturer" is available for purchase on my device. Since this testing is volunteer work, I'm not planning to make the purchase. Is there an alternative way to verify the HEVC-dependent behavior without purchasing the paid version?

    As a side note, uploading a ~1MB JPEG currently takes about 10 seconds on my setup, in case that's useful context for the timeout discussion.

    Image
  7. singhakanshu00 commented on Aug 5, 2026

    @singhakanshu00
    Contributor

    PR is up: #81211

    Fix:
    one of the three client-side HEIC decode strategies in canvas-utils.ts wasn't wrapped in try/catch like the other two. When ImageDecoder.isTypeSupported() optimistically reports HEIC support without the actual codec present, decoder.decode() never settles instead of rejecting — hanging the upload forever with no error. Added a timeout that force-closes the decoder so it falls through to the next strategy (or a clean error) instead.

    On the timeout value: @369work's ~10s is total upload time (network + server processing), not decode time — a working decode finishes in well under a second, so per @adamsilverstein's feedback that 8s felt too long, I went with 3s: comfortably above realistic decode time, but short enough not to stall the user. Not derived from a formal benchmark, just a reasoned estimate — open to adjusting if there's better data.

    Tested on Mac by simulating the exact "claims support, then hangs" condition via DevTools (no real Windows box without the HEVC extension available to me). Confirms the hang on trunk and the fix recovering within ~3s, sub-sizes generating correctly.

  8. adamsilverstein commented on Aug 5, 2026

    @adamsilverstein
    Member

    Thanks for the additional details @369work and confirming the fix @singhakanshu00 .

    Regarding installing the HEVC Video Extensions from Device Manufacturer extension, based on my reading of reddit threads, this is often installable for free or costs less than 1 $. Is that what you were seeing when you tried (I don't even see the install page on my mac).

    @369work I understand not wanting to buy something for volunteer work, makes perfect sense! That said, I'd love to have real world confirmation from you that installing the extension fixes the uploader for you and would happily send you the $1 if that is indeed what it costs. Part of my time is sponsored (and part volunteer) so I am happy to pay for things like this.

  9. 7 remaining items

  10. pbiron commented on Aug 20, 2026

    @pbiron

    Reproduction Report

    Environment

    • WordPress: 7.1
    • Subdirectory: No
    • PHP: 8.4.21
    • Server: Apache
    • Database: mysqli (Server: 8.4.5 / Client: mysqlnd 8.4.21)
    • Browser: Edge 151.0.0.0
    • OS: Windows 11
    • Theme: Twenty Twenty-Five 1.5
    • MU Plugins:
      • None
    • Plugins:
      • Query Monitor 4.0.7
      • Test Reports 1.3.1

    The above is a hand-rolled WAMP-like stack, with server and browser running on the same Win11 machine.

    Initially I couldn't reproduce, the sample HEIC image was uploaded and sub-sizes were created (I haven't yet figured out how to tell whether the image was covered to JPG in the browser or on the server), but no hanging occurred. After adding an img block, I first tried clicking on the Upload button and selecting the .heic file on disk. That worked fine, no hanging. I then added another img block and clicked on the Media Library button and dragged-dropped the .heic file into the ML and that worked fine, no hanging.

    I do not have the HEIC Video Extension installed, but I do have Imagick, which understands HEIC

    Image

    So, I wrote a little one-line plugin to force use of GD and was then able to reproduce the hanging behavior when clicking the Upload button. When clicking the Media Library button and dragging-dropping an .heic file, there is no hanging but the img block shows a "broken image" icon:

    Image
  11. pbiron commented on Aug 20, 2026

    @pbiron

    Note: after doing this testing, I think it'd be a good idea for the Test Reports plugin to also report which image editors are available. When I get a chance, I'll get with Andy and Colin and propose that to them.

  12. adamsilverstein commented on Aug 23, 2026

    @adamsilverstein
    Member

    So, I wrote a little one-line plugin to force use of GD and was then able to reproduce the hanging behavior when clicking the Upload button. When clicking the Media Library button and dragging-dropping an .heic file, there is no hanging but the img block shows a "broken image" icon:

    Thanks for reproducing @pbiron - that sounds right, when you had Imagick as your default the system chose the server side path. Without it, you hit the bug.

    Next, can you check if #81211 resolves the issue for you?

  13. pbiron commented on Aug 23, 2026

    @pbiron

    Next, can you check if #81211 resolves the issue for you?

    Yes, I will try later today.

  14. 369work commented on Sep 4, 2026

    @369work
    ContributorAuthor

    Follow-up from my original report, with two new findings. Both came out of testing #82409, which had me try the Media Library modal upload path and re-check this on a machine with the codecs installed — neither of which I had covered when I first filed this.

    1. The hang also happens with the codecs installed.

    I originally suspected the missing Windows HEVC Video Extension was the trigger. I have since tested with both "HEIC Image Extensions" and "HEVC Video Extensions" installed, and the upload hangs in exactly the same way — the spinner never stops, the raw .heic is saved to wp-content/uploads, no sub-sizes are generated, and the Media Library shows a gray placeholder. Reproduced in Chrome 152 and Edge, and identically whether the file is dropped directly onto an Image block or onto the Media Library modal.

    So the stall does not appear to be limited to systems lacking OS-level codec support. It may be worth confirming that #81211 covers this case too.

    2. debug.log records PHP warnings that the browser console does not show.

    The console stays silent, but these are logged at the moment of upload:

    [04-Sep-2026 05:13:48 UTC] PHP Warning:  Undefined array key "width" in ...\wp-admin\includes\image.php on line 110
    [04-Sep-2026 05:13:48 UTC] PHP Warning:  Undefined array key "height" in ...\wp-admin\includes\image.php on line 111
    

    These two warnings are the only entries logged for the upload — no fatal error. The server-side path does run, but it cannot read the HEIC dimensions, which matches the sub-sizes never being generated.

    That raises a question about the fallback in #81211: once the client-side decode fails fast and hands off to server-side processing, does the server side actually generate usable sub-sizes for HEIC in this environment, or does it hit the same dimension-reading problem? Happy to test the patch if that would help.

    Environment for this round of testing:

    • WordPress: 7.1
    • PHP: 8.4.24 (built-in development server)
    • Database: WP_MySQL_On_SQLite (Server: 8.0.38 / Client: 3.53.2)
    • Theme: Twenty Twenty-Five 1.5
    • Browser: Chrome 152.0.0.0, also reproduced in Edge
    • OS: Windows 11, with HEIC Image Extensions and HEVC Video Extensions installed
    Image Image Image Image Image
  15. adamsilverstein commented on Sep 5, 2026

    @adamsilverstein
    Member

    Thanks for the additional testing @369work - does the fix in #81211 correct the issue for you even with the codec support installed??

  16. 369work commented on Sep 5, 2026

    @369work
    ContributorAuthor

    Yes — #81211 fixes it for me, with the codecs installed.

    Dropping a .heic file on an Image block now converts it to .jpg and renders it in the editor. The spinner runs briefly and completes instead of spinning forever. Same result in Chrome 152 and Edge.


    Image Image

    On the fallback question I raised earlier: my environment has no ImageMagick, so I expected an error rather than a conversion. It didn't need one — with the codecs installed, the conversion happens in the browser, so the server-side path is never reached.

    The image.php width/height warnings I quoted in my earlier comment no longer appear in debug.log either — it stays clean through both upload paths.

    If you drag it into a media modal, Media Library modal shows no spinner at all and ends silently, storing the .heic unconverted — the block then shows a broken image.


    Image Image

    That is unchanged before and after this patch.
    In the Media Library the two uploads sit side by side.
    The one from the modal is a gray placeholder, the one dropped on the block has a working thumbnail.
    When I returned to the site editor, an error was displayed.
    'This image has an empty alt attribute;'


    Image

    This is the behaviour described in #82409, which this PR doesn't touch, so I mention it only to be clear about the scope of my test.

    Environment

    • WordPress: 7.1
    • Gutenberg: plugin built from this PR's branch (reports as 23.7.1)
    • PHP: 8.4.24 (built-in development server), no ImageMagick
    • Database: WP_MySQL_On_SQLite (Server: 8.0.38 / Client: 3.53.2)
    • Browser: Chrome 152.0.0.0 and Edge — identical results
    • OS: Windows 11, with HEIC Image Extensions and HEVC Video Extensions installed
    • Theme: Twenty Twenty-Five 1.5
    • MU Plugins:
      • 99-studio-loader.php
    • Plugins:
      • Query Monitor 4.0.7
      • Test Reports 1.3.1
  17. moved this from 🏗️ In Progress to ✅ Done in WordPress 7.1.x Editor Taskson Sep 22, 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