Make WordPress Core

Opened 5 months ago

Last modified 4 weeks ago

#64902 new defect (bug)

wp_save_image() fatal error when wp_save_image_file() returns WP_Error

Reported by: notglossy Owned by:
Priority: normal Milestone: Awaiting Review
Component: Media Version: 6.9.4
Severity: normal Keywords: has-patch needs-testing has-test-info
Cc: Focuses:

Description

wp_save_image_file() has a return type of array|WP_Error|bool.

wp_save_image() checks the return value of wp_save_image_file() with if ( ! $saved_image ) (line1020), but WP_Error is truthy, so it passes through.

Line 1054 then accesses $saved_image['filesize'] which fatals with "Cannot use object of type WP_Error as array".

This is reproducible when any image editor's save() returns WP_Error (e.g., unsupported format, disk full, encoder failure)

Affects all editors (GD, Imagick, custom) since save() is documented as @return array|WP_Error

This bug was discovered when building a plugin which extends WP_Image_Editor to take advantage of a different graphics library.

Attachments (1)

64902.patch (1.3 KB ) - added by yasirkhalifa 5 months ago.

Download all attachments as: .zip

Change History (9)

@yasirkhalifa
5 months ago

#1 @yasirkhalifa
5 months ago

Hi,

I’ve attached a patch to properly handle WP_Error returned by wp_save_image_file().

Currently, wp_save_image_file() may return a WP_Error, but wp_save_image() only checks for falsy values. Since WP_Error is a truthy object, execution continues and leads to a fatal error when accessing array keys like $saved_imagefilesize.

Proposed fix:

  • Add an explicit is_wp_error() check immediately after the wp_save_image_file() call.
  • Return the error message safely using esc_js() for proper handling in the UI.

This prevents fatal errors and ensures graceful error handling consistent with WordPress coding standards.

Steps to reproduce:

  1. Trigger a failure in the image editor (e.g., unsupported format or editor save failure).
  2. wp_save_image_file() returns WP_Error.
  3. Without this patch → fatal error occurs.
  4. With this patch → error is handled and returned properly.

Feedback welcome. Happy to iterate if needed.

Thanks!

#2 @yasirkhalifa
5 months ago

  • Keywords has-patch needs-testing added

#3 follow-up: @aytor
4 months ago

I tested the bug on wordpress with environemnt:
WordPress: 6.9.4
PHP: 8.2
Local WP

The bug is not reproducing, I'm getting the message saying "Could not load the preview image. Please reload the page and try again." but is not allowing me to save it the button is grayed out.

#4 in reply to: ↑ 3 @notglossy
4 months ago

Yes, this is one of the symptoms on the front end since the whole process fails because WP_Error is not an array and returns a 500 error.

Replying to aytor:

I tested the bug on wordpress with environemnt:
WordPress: 6.9.4
PHP: 8.2
Local WP

The bug is not reproducing, I'm getting the message saying "Could not load the preview image. Please reload the page and try again." but is not allowing me to save it the button is grayed out.

#5 @ekamran
3 months ago

Test Report

Patch tested: attachment:64902.patch

Environment

  • Reported version: 6.9.4
  • Tested version: 7.1-alpha-62161-src
  • PHP: 8.3.31
  • Environment: wordpress-develop local Docker environment
  • Test method: WP-CLI automated reproduction using wp_save_image_editor_file
  • OS: macOS

Reproduction

I was able to confirm that the reported issue is still reproducible on current trunk.

The test creates an image attachment from tests/phpunit/data/images/test-image.jpg, sets up a scale operation for wp_save_image(), and uses the existing wp_save_image_editor_file filter to force wp_save_image_file() to return a WP_Error.

Clean trunk output:

attachment_id=10
source_size=50x50
request={"do":"scale","fwidth":25,"fheight":25,"target":"all"}

Fatal error: Uncaught Error: Cannot use object of type WP_Error as array
in /var/www/src/wp-admin/includes/image-edit.php:1054

This confirms that when wp_save_image_file() returns WP_Error, wp_save_image() continues as if it received an array and then fatals when reading $saved_image['filesize'].

Patch Result

After applying 64902.patch locally and running the same automated reproduction, the fatal error no longer occurs. wp_save_image() returns a normal error response:

attachment_id=11
source_size=50x50
request={"do":"scale","fwidth":25,"fheight":25,"target":"all"}
result={
    "error": "Forced save failure for #64902 retest."
}

Tests

I also ran the related PHPUnit tests with the patch applied:

npm run test:php -- --group ajax --filter Tests_Ajax_wpAjaxImageEditor

OK (4 tests, 9 assertions)
npm run test:php -- --filter Tests_Image_Functions

OK (113 tests, 211 assertions)

Notes

The patch handles the WP_Error return from wp_save_image_file() before the code accesses $saved_image['filesize'], which prevents the fatal and returns a normal error response instead.

A focused PHPUnit test using the wp_save_image_editor_file filter to force a WP_Error return would be a useful follow-up for this patch.

#6 @jamesbregenzer
2 months ago

  • Keywords has-test-info added

Tested against current trunk (7.1-alpha).

I was able to reproduce the fatal error by forcing wp_save_image_file() to return a WP_Error, matching the failure path described in the ticket.

After applying the patch, the fatal error no longer occurred and the WP_Error message was returned as the image edit error response.

I was unable to identify any regressions during testing.

Patch tests successfully for me.

#7 @yasirkhalifa
5 weeks ago

Hi everyone, just checking whether there is anything else needed from my side for this ticket. I'd be happy to update the patch or address any feedback if necessary. Thanks!

#8 @eishanoor
4 weeks ago

Manual Test Report

I manually tested the proposed patch for ticket #64902.

Test environment

  • WordPress: 7.0.1
  • PHP: 8.3.30
  • Local environment: Laragon
  • Operating system: Windows
  • Browser: Google Chrome
  • Image format: PNG

Test method

I created a temporary must-use plugin using the
wp_save_image_editor_file filter to force the image-saving
operation to return a WP_Error.

I tested the image editor before and after manually applying the
proposed patch to wp-admin/includes/image-edit.php.

Before applying the patch

With the test must-use plugin active, the image editor continued
loading after clicking Save, and the image was not saved.

After applying the patch

With the test must-use plugin active, the save request completed
immediately instead of continuing to load, and the WP_Error was
handled without the previous failure.

I then disabled the temporary must-use plugin and confirmed that
normal image editing and saving worked successfully.

Result

The patch works as expected in my manual testing. I did not identify
any regression in the normal image-saving workflow.

Note: See TracTickets for help on using tickets.