I was rebuilding several pages of a client site in PHP, and part of that process registered dozens of SVG icons into the Media Library programmatically. Not manual uploads through the dashboard, but from code: read the SVG file from the theme, call wp_insert_attachment(), store the returned ID, then use that ID as the icon in an Elementor widget. Everything looked fine. Pages rendered, icons showed up, nothing broke in front of a visitor.
The problem only surfaced when I opened debug.log. Every page that used those icons spat out the same line, over and over:
PHP Warning: Undefined array key "file" in /wp-includes/media.php on line ...A single page could emit this warning a dozen times, once per icon. A log that used to be clean ballooned into hundreds of lines a day. And because WP_DEBUG_DISPLAY was still on in staging, the PHP notice leaked out on top of some icons and wrecked the layout. The frontend "worked", but something was clearly wrong underneath.
The dead ends
My first reaction aimed at the wrong target. Because SVG was involved, I immediately suspected the plugin that handles SVG support. I disabled it for a bit, and the warning stayed. So it was not that.
Suspect number two: Elementor, since the icons lived inside its widgets. But blaming Elementor felt too easy, and turning Elementor off is the same as turning off half the site. Not a solution.
Since the warning pointed at a WP core file, wp-includes/media.php, I opened the line it named. There sat an access to $meta['file'] inside the wp_get_attachment_metadata() path. That was the key: Undefined array key "file" is PHP 8 speak for "you read an array key that does not exist". So the real question was not "why is media.php broken", it was "why does my attachment metadata have no file key".
The root cause
This is where it untangled. wp_insert_attachment() turned out to be less complete than I assumed. The function creates the attachment post and sets _wp_attached_file, the relative path to the file, but it does NOT populate _wp_attachment_metadata. For raster images, that second step is normally done by wp_generate_attachment_metadata(), which produces an array holding file, width, height, and the list of generated sizes. Because I only called wp_insert_attachment() and stopped there, that metadata never existed.
For SVG, a lot of people deliberately skip wp_generate_attachment_metadata() because SVG is not raster: there are no pixel dimensions to measure, no thumbnails to generate. So skipping feels reasonable. The consequence, though, is that _wp_attachment_metadata for each SVG is empty.
Then Elementor enters. When a widget uses a custom image_size, Elementor calls wp_get_attachment_metadata() for that attachment, then immediately reads $meta['file'] to compute a path. The metadata is empty, the file key is missing, and PHP 8 fires Undefined array key "file" from inside media.php, every time, per icon, on every render. Elementor was not buggy and media.php was not broken. What was missing was the metadata I should have written myself right after registering each attachment.
The fix
The fix is straightforward once the root cause is clear: after each programmatic attachment registration, write _wp_attachment_metadata explicitly, and make sure the file key exists and matches _wp_attached_file.
$attached_file_path = get_post_meta( $att_id, '_wp_attached_file', true );
update_post_meta( $att_id, '_wp_attachment_metadata', array(
'file' => $attached_file_path, // must match _wp_attached_file
'width' => 0,
'height' => 0,
'mime-type' => 'image/svg+xml',
) );The easy-to-miss part: the file value in metadata has to match the _wp_attached_file value exactly. If they differ, WordPress gets confused resolving the path and you can trade one problem for another. For SVG, width and height can stay 0 since they are not measured. What matters is that the key exists so $meta['file'] is no longer undefined.
For raster files like PNG and JPG, do not hand-write the metadata like this. Call the built-in WordPress function that computes everything correctly:
require_once ABSPATH . 'wp-admin/includes/image.php';
$metadata = wp_generate_attachment_metadata( $att_id, get_attached_file( $att_id ) );
wp_update_attachment_metadata( $att_id, $metadata );wp_generate_attachment_metadata() is not loaded on the frontend by default, which is why you have to require_once the wp-admin/includes/image.php file first. This function is the one that fills in file, width, height, plus every generated size, so for raster this is the correct path, not a hand-written array.
I ran this fix across every asset registered by code: 37 SVGs through explicit metadata writes, and 5 PNGs through wp_generate_attachment_metadata(). After that debug.log went clean, the notices in the layout vanished, and not a single Undefined array key "file" remained.
Takeaways
wp_insert_attachment()does not populate_wp_attachment_metadata. It only creates the attachment post and sets_wp_attached_file.- If you register attachments programmatically, writing the metadata is your responsibility.
- For SVG, write
_wp_attachment_metadataby hand with afilekey that matches_wp_attached_file;widthandheightcan be 0. - For raster, use
wp_generate_attachment_metadata()afterrequire_onceonwp-admin/includes/image.php, not a hand-written array. Undefined array key "file"inwp-includes/media.phpalmost always means empty attachment metadata, not a WP core or Elementor bug.- Do not silence
WP_DEBUGjust to hide the notice. That notice is telling you an attachment is half-finished.
