Make WordPress Core

Opened 3 months ago

Closed 2 months ago

#65748 closed defect (bug) (fixed)

Image editor: fatal error when attachment metadata has no `sizes` array

Reported by: josephscott Owned by: westonruter
Priority: normal Milestone: 7.1
Component: Media Version:
Severity: normal Keywords: has-patch has-unit-tests
Cc: Focuses:

Description

I noticed some fatal errors in wp_save_image() on a site recently.

Uncaught TypeError: array_merge(): Argument #1 must be of type array, null given

That came from this line - ​https://github.com/WordPress/wordpress-develop/blame/trunk/src/wp-admin/includes/image-edit.php#L1132

$meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) );

While I suspect this is something wrong or a mistake with the image, after looking at the code WordPress needs to make sure that $meta['sizes'] exists before using it.

There is a similar issue with wp_restore_image().

Change History (6)

This ticket was mentioned in ​PR #12744 on ​WordPress/wordpress-develop by ​@josephscott.


3 months ago
#1

  • Keywords has-patch has-unit-tests added

https://core.trac.wordpress.org/ticket/65748

AI assistance: Yes
Tool(s): Claude
Model(s): Opus 5
Used for: Writing the tests

#2 @westonruter
3 months ago

  • Milestone Awaiting Review → 7.1
  • Owner set to westonruter
  • Status new → reviewing

​nazmulasif commented on ​PR #12744:


3 months ago
#3

## Test Report
Patch tested: ​https://github.com/WordPress/wordpress-develop/pull/12744

### Environment

  • WordPress: 7.1-beta3-20260728.201755
  • Subdirectory: No
  • PHP: 7.4.33
  • Server: PHP.wasm
  • Database: WP_SQLite_Driver (Server: 8.0.38 / Client: 3.51.0)
  • Browser: Chrome 150.0.0.0
  • OS: macOS
  • Theme: Twenty Twenty-Five 1.5
  • MU Plugins: None activated
  • Plugins:
    • Test Reports 1.3.0

### Steps taken

  1. Launched the WordPress Playground instance built from PR #12744.
  2. Navigated to Media → Library and uploaded a test image.
  3. Clicked Edit Image to open the core image editor interface.
  4. Performed Crop and Scale operations, then saved the modifications.
  5. Clicked Restore Original Image to revert the image back to its original state.
  6. Verified that no PHP fatal errors or warnings occurred during the process.
  7. ✅ Patch is solving the problem

### Expected result

  • Image saving, cropping, scaling, and restoring operations should execute successfully without throwing PHP type errors or fatal errors when processing attachment metadata.

### Additional Notes

  • Tested via WordPress Playground instance. Crop, scale, and restore functionalities work as expected without any errors.

### Screenshots/Screencast with results

  • Screenshot/Screencast before: N/A
  • Screenshot/Screencast after: N/A

### Support Content

This ticket was mentioned in ​Slack in #core-test by noruzzaman. ​View the logs.


3 months ago

#5 @ugyensupport
2 months ago

Test Report

Patch tested: ​https://github.com/WordPress/wordpress-develop/pull/12744
Ticket: https://core.trac.wordpress.org/ticket/65748

Environment

  • WordPress: 7.0
  • Subdirectory: No
  • PHP: 8.5.8
  • Server: PHP built-in server (WP-CLI wp server)
  • Database: SQLite (Studio)
  • Browser: Chromium (Playwright)
  • OS: macOS
  • Theme: Twenty Twenty-Five 1.5
  • MU Plugins: 99-studio-loader (Studio)
  • Plugins:
    • Test Reports 1.3.0


Steps taken

1.Uploaded a JPEG to the Media Library, then removed the sizes key from its
attachment metadata (reproducing the reported "no sizes array" state).

2.On UNPATCHED core: opened the attachment → Edit Image → Rotate 90° left →
Save Edits. → admin-ajax image-editor (do=save) returned HTTP 500
"There has been a critical error on this website."
The editor froze mid-save. Root cause: TypeError, array_merge() at image-edit.php:1127.

3.Applied the patch (guards in wp_save_image() + wp_restore_image()); confirmed php -l reports no syntax errors and both guards are present.

4.Reset the attachment metadata back to "no sizes", reloaded the editor, and
repeated the exact same edit (Rotate 90° left → Save Edits).
→ the identical request now returns {"success":true,"msg":"Image saved"}; the "Image saved" notice shows and the rotated image is displayed.

5.Additionally verified via WP-CLI against all 5 metadata variants from the PR's data provider — missing key / null / / 'not-an-array' / false — for BOTH wp_save_image() and wp_restore_image(), plus the non-array-metadata guard and the normal valid-metadata path.

✅ Patch is solving the problem

Expected result

  • When an attachment's metadata has no usable sizes array, saving or restoring an image in the editor should NOT throw a fatal. The functions should normalize the missing/invalid sizes to an empty array and complete the edit (regenerating sub-sizes), or return a graceful "Cannot load image metadata." message when the metadata itself is unusable — never a TypeError.

Additional Notes

  • Pre-fix: wp_save_image() fatals for ALL five variants at array_merge() (image-edit.php:1127). wp_restore_image() fatals for scalar-string sizes at :878; on PHP 8.5, null/false/missing happen to auto-vivify into an array, but the guard is still needed — writing to a false offset is deprecated as of PHP 8.1 and an error as of PHP 9, so those cases become fatals on newer runtimes.
  • Post-fix: 10/10 cases pass (2 functions × 5 variants); non-array metadata returns a graceful error; the normal valid-metadata path is unchanged (no regression).
  • Verified through the actual browser image-editor UI AND via WP-CLI for full, deterministic variant coverage.
  • The core file was backed up and restored to its original checksum after testing; the disposable admin user and test attachment were deleted.

Screenshots/Screencast with results

Support Content

  1. Reproduce the "no sizes array" state (WP-CLI):

wp eval '
require_once ABSPATH."wp-admin/includes/image.php";
$up = wp_upload_bits("demo.jpg", null, file_get_contents("/path/to/any.jpg"));
$id = wp_insert_attachment(

array("post_mime_type"=>"image/jpeg","post_status"=>"inherit"),
$upfile

);

$meta = wp_generate_attachment_metadata($id, $upfile);
unset($metasizes); <-- the bug trigger
wp_update_attachment_metadata($id, $meta);
echo "Attachment ID: $id\n";
'
# Then: Media → edit this attachment → Edit Image → Rotate → Save Edits.

  1. The patch (src/wp-admin/includes/image-edit.php):

wp_restore_image(), after the $backup_sizes check:
if ( ! is_array( $meta ) ) {

$msg->error = ( 'Cannot load image metadata.' );
return $msg;

}

if ( ! isset( $metasizes )
! is_array( $metasizes ) ) {

$metasizes = array();

}

wp_save_image(), before the array_merge():

if ( ! isset( $metasizes )
! is_array( $metasizes ) ) {

$metasizes = array();

}

  1. Data-provider variants exercised (both functions): 'no sizes key' | 'null' | 'empty string' | 'string' | 'boolean(false)'
Last edited 2 months ago by ugyensupport (previous) (diff)

#6 @westonruter
2 months ago

  • Resolution → fixed
  • Status reviewing → closed

In 63002:

Media: Normalize unusable sizes attachment metadata.

Attachment metadata is untyped, and the sizes key is not guaranteed to be present or to hold an array. Sub-size generation can leave it out entirely, and a plugin filtering wp_get_attachment_metadata can replace it with anything. wp_save_image() validated only that the metadata itself was an array before passing $meta['sizes'] to array_merge(), so an absent or scalar value raised a TypeError and the image editor returned an HTTP 500 mid-save. wp_restore_image() had the same gap at $meta['sizes'][ $default_size ] = $data, where a string raises "Cannot use a scalar value as an array" and false is deprecated as of PHP 8.1 and an error as of PHP 9.

wp_get_attachment_metadata() now returns false whenever the metadata is not an array, on the $unfiltered path as well as after the filter, matching the documented array|false return. A sizes key holding a non-array is replaced with an empty array, so every caller can rely on the key being an array whenever it is present. The key is not invented when it is absent: audio, video and document attachments legitimately store metadata without it, and callers such as wp-admin/post.php read the metadata unfiltered in order to modify it and write it back, so normalizing there would persist into the database.

The image editor entry points fill in the missing key themselves, and wp_prepare_attachment_for_js() now checks the dimensions of the full entry alongside its filename before reading them, removing the "Undefined array key" warnings raised for a sizes array that carries no usable full size. PHPUnit coverage is added for all three functions.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/12744.
Follow-up to r11965, r23873, r38949, r49084, r62978.

Props josephscott, westonruter, mukesh27, irozum, ugyensupport, nazmulasif.
See #65686, #64898.
Fixes #65748.

Note: See TracTickets for help on using tickets.