Make WordPress Core

Opened 6 days ago

Last modified 68 minutes ago

#47872 new enhancement

Try to create image sub-sizes again when uploading fails with a PHP fatal error

Reported by: azaozz Owned by:
Milestone: 5.3 Priority: normal
Severity: normal Version:
Component: Upload Keywords: has-patch needs-testing needs-dev-note
Focuses: Cc:


A common reason uploading of images fails is because the server runs out of resources while creating image sub-sizes. That results in either a timeout or out of memory errors.

In these cases the image file has been uploaded successfully but some or all of the sub-sizes haven't been created. Because of the PHP fatal error, the response to the browser/client is an "Internal Server Error" (HTTP 500) which currently results in showing an unfriendly/non-actionable "HTTP Error" message in the UI. It also leaves the "orphaned" image sub-sizes that were created before the server crash in the upload directory.

After #40439 and [45538] it became possible to create the missing image sub-sizes when an image file was uploaded successfully. Ideally that should happen right after the file was uploaded, so the user has access to all expected sub-sizes.

Attachments (1)

47872.diff (45.3 KB) - added by azaozz 6 days ago.

Download all attachments as: .zip

Change History (6)

6 days ago

#1 @azaozz
6 days ago

  • Keywords has-patch needs-testing needs-dev-note added

In 47872.diff:

  • When an image upload fails with HTTP 500 error do another AJAX request to try to create any missing image sub-sizes. This currently works in the Media modal and on the Media Library screen, including support for the old inline uploader in Media => Add New. This is attempted three more times.
  • Uses a temporary uploaded file reference (for images only) to retrieve the new attachment_id (saved in a transient for 6 hours).

Currently there are no specific UI changes but the HTTP 500 errors are visible in the browser console. If it still fails after four attempts (the initial upload plus three re-tries) it shows the error messages that come from the server, if any. Then it falls back to the default HTTP 500 error (the text was updated to better reflect what's going on).

Testing is very much appreciated, although it's not very straightforward. To test best would be to artificially "crash" PHP.

  1. Set max_execution_time=4 in php.ini to cause a timeout. Upload a large image, something like 5 - 6MB and watch the request in the browser tools "network" tab. If it doesn't time out, try larger image or set the max_execution_time to 3 seconds (and don't forget to put it back to the original value when done, default max_execution_time is 30 seconds).
  1. Set memory_limit=64M in php.ini and add define( 'WP_MAX_MEMORY_LIMIT', '64M' ); in wp-config.php to limit the memory. Again, try uploading a bigger image and watch the browser tools for HTTP 500 responses. Couple of tests are possible here. If the memory is low and the image big, it may run out of memory on the initial loading of the image. Then there will be 4 HTTP 500 errors pretty fast one after the other. In that case if you bump the memory limit up by a few MB it will start failing while creating the sub-sizes. Then the result will be several requests the last of which should be a "success".
Last edited 5 days ago by azaozz (previous) (diff)

This ticket was mentioned in Slack in #core-media by azaozz. View the logs.

6 days ago

#3 @azaozz
6 days ago

Related: #39647, #40439.

This ticket was mentioned in Slack in #core-restapi by azaozz. View the logs.

4 days ago

#5 @azaozz
68 minutes ago

There is an alternative way I've been testing, noting it here for completeness. Instead of sending an "upload reference" with the upload request, it could try to respond with the new attachment_id right after the attachment was created and "flush" the PHP output (using fastcgi_finish_request()), and continue making image sub-sizes in the background.

That would provide a bit better "responsiveness" in the UI but comes with some strings attached: no sub-size will be immediately available. This method would need quite a lot more "processing logic" in the client. For example if the user just wants to insert an image into a post the client would have to use the original image at first, then request a particular size, and if it was created, replace the image.

If the required sub-size is not yet created, the client will have to try again later. That process may take quite long on slow/busy servers, 30 seconds or more. In the background if PHP fails to make all sub-sizes, we can re-try on the subsequent requests from the client (as we know the attachment_id). A pretty complex case would be when the users don't wait that long and close the browser or navigate away. Then all the "replace" logic should happen after the fact on the server, perhaps with a cron job, etc.

From a purely UI/UX perspective thinking that waiting until all sub-sizes are created (the current behaviour) makes more sense. Then all sub-sizes will be available and the users can pick the one they need. When uploading a file, it takes some time and the users expect that. Largely that time will be for the actual upload to go through, not for creating the sub-sizes.

Note: See TracTickets for help on using tickets.