Make WordPress Core

Opened 11 months ago

Last modified 5 weeks ago

#64236 reopened defect (bug)

Warning messages when deleting any plugin in WP 6.9 RC1

Reported by: leemon Owned by:
Priority: normal Milestone:
Component: Plugins Version: 6.9
Severity: normal Keywords:
Cc: Focuses:

Description

When deleting any plugin from the admin interface in WP 6.9 RC1, a "Deletion failed" message is displayed alongside several PHP Warning notices related to missing language files (.l10n.php) for the deleted plugin. Despite the error messages, the plugin is successfully removed from the file system and the database. I've tried in two different hosting services, and this issue only happens in one of them.

Deletion failed: 
Warning:  include(/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-ca.l10n.php): Failed to open stream: No such file or directory in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(): Failed opening '/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-ca.l10n.php' for inclusion (include_path='.:/opt/remi/php83/root/usr/share/pear:/opt/remi/php83/root/usr/share/php:/usr/share/pear:/usr/share/php') in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-es_ES.l10n.php): Failed to open stream: No such file or directory in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(): Failed opening '/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-es_ES.l10n.php' for inclusion (include_path='.:/opt/remi/php83/root/usr/share/pear:/opt/remi/php83/root/usr/share/php:/usr/share/pear:/usr/share/php') in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-ca.l10n.php): Failed to open stream: No such file or directory in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(): Failed opening '/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-ca.l10n.php' for inclusion (include_path='.:/opt/remi/php83/root/usr/share/pear:/opt/remi/php83/root/usr/share/php:/usr/share/pear:/usr/share/php') in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-es_ES.l10n.php): Failed to open stream: No such file or directory in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618

Warning:  include(): Failed opening '/srv/vhost/example.com/home/html/wp-content/languages/plugins/better-search-replace-es_ES.l10n.php' for inclusion (include_path='.:/opt/remi/php83/root/usr/share/pear:/opt/remi/php83/root/usr/share/php:/usr/share/pear:/usr/share/php') in /srv/vhost/example.com/home/html/wp-includes/l10n.php on line 1618
{"success":true,"data":{"delete":"plugin","slug":"better-search-replace","plugin":"better-search-replace\/better-search-replace.php","pluginName":"Better Search Replace"}}

Site Health Info:

### wp-core ###

version: 6.9-RC1-61214
site_language: ca
user_language: en_US
timezone: Europe/Madrid
permalink: /%year%/%monthnum%/%day%/%postname%/
https_status: true
multisite: false
user_registration: 0
blog_public: 1
default_comment_status: closed
environment_type: production
user_count: 2
dotorg_communication: true

### wp-paths-sizes ###

wordpress_path: /srv/vhost/example.com/home/html
wordpress_size: 54,48 MB (57130374 bytes)
uploads_path: /srv/vhost/example.com/home/html/wp-content/uploads
uploads_size: 300,00 MB (314575729 bytes)
themes_path: /srv/vhost/example.com/home/html/wp-content/themes
themes_size: 1,98 MB (2077342 bytes)
plugins_path: /srv/vhost/example.com/home/html/wp-content/plugins
plugins_size: 47,79 MB (50112616 bytes)
fonts_path: /srv/vhost/example.com/home/html/wp-content/uploads/fonts
fonts_size: directory not found
database_size: 95,86 MB (100515840 bytes)
total_size: 500,12 MB (524411901 bytes)

### wp-active-theme ###

name: Custom Child Theme (customchildtheme)
version: 1.0.0
author: Custom User
author_website: http://customuser.com/code/
parent_theme: Custom Theme (customtheme)
theme_features: widgets-block-editor, post-thumbnails, menus, editor-style, custom-logo, automatic-feed-links, title-tag, html5, editor-styles, responsive-embeds, customize-selective-refresh-widgets, custom-header, woocommerce, wc-product-gallery-slider, widgets
theme_path: /srv/vhost/example.com/home/html/wp-content/themes/customchildtheme
auto_update: Disabled

### wp-parent-theme ###

name: Custom Theme (customtheme)
version: 3.0
author: Custom User
author_website: https://customuser.com/code/
theme_path: /srv/vhost/example.com/home/html/wp-content/themes/customtheme
auto_update: Disabled

### wp-themes-inactive (1) ###

Twenty Twenty-Four: version: 1.3, author: the WordPress team, Auto-updates enabled

### wp-plugins-active (14) ###

BBQ Firewall: version: 20250324, author: Jeff Starr, Auto-updates enabled
Classic Widgets: version: 0.3, author: WordPress Contributors, Auto-updates enabled
Extended Coupon Features for WooCommerce FREE: version: 3.4.0, author: Soft79, Auto-updates enabled
Limit Login Attempts Reloaded: version: 2.26.24, author: Limit Login Attempts Reloaded, Auto-updates enabled
Polylang: version: 3.7.5, author: WP SYNTEX, Auto-updates enabled
Polylang for WooCommerce: version: 2.1.5, author: WP SYNTEX, Auto-updates enabled
Rather Simple Mailchimp: version: 2.0, author: Custom User, Auto-updates disabled
Rather Simple Social Menu: version: 3.0, author: Custom User, Auto-updates disabled
Rather Simple WooCommerce Alternate Product Categories: version: 2.0, author: Custom User, Auto-updates disabled
The SEO Framework: version: 5.1.2, author: The SEO Framework Team, Auto-updates enabled
Two Factor: version: 0.14.1, author: WordPress.org Contributors, Auto-updates enabled
WooCommerce: version: 10.3.4, author: Automattic, Auto-updates enabled
WooCommerce Redsys Gateway Light: version: 6.5.0, author: José Conti, Auto-updates enabled
YITH WooCommerce Gift Cards: version: 4.29.0, author: YITH, Auto-updates enabled

### wp-media ###

image_editor: WP_Image_Editor_Imagick
imagick_module_version: 1693
imagemagick_version: ImageMagick 6.9.13-25 Q16 x86_64 18639 https://legacy.imagemagick.org
imagick_version: 3.8.0
file_uploads: 1
post_max_size: 32M
upload_max_filesize: 32M
max_effective_size: 32 MB
max_file_uploads: 20
imagick_limits: 
	imagick::RESOURCETYPE_AREA: 128 GB
	imagick::RESOURCETYPE_DISK: 9.2233720368548E+18
	imagick::RESOURCETYPE_FILE: 49152
	imagick::RESOURCETYPE_MAP: 64 GB
	imagick::RESOURCETYPE_MEMORY: 32 GB
	imagick::RESOURCETYPE_THREAD: 1
	imagick::RESOURCETYPE_TIME: 0
imagemagick_file_formats: 3FR, 3G2, 3GP, AAI, AI, APNG, ART, ARW, AVI, AVS, BGR, BGRA, BGRO, BIE, BMP, BMP2, BMP3, BRF, CAL, CALS, CANVAS, CAPTION, CIN, CIP, CLIP, CMYK, CMYKA, CR2, CR3, CRW, CUR, CUT, DATA, DCM, DCR, DCX, DDS, DFONT, DNG, DOT, DPX, DXT1, DXT5, EPDF, EPI, EPS, EPS2, EPS3, EPSF, EPSI, EPT, EPT2, EPT3, ERF, EXR, FAX, FILE, FITS, FLV, FRACTAL, FTP, FTS, G3, G4, GIF, GIF87, GRADIENT, GRAY, GRAYA, GROUP4, GV, H, HALD, HDR, HISTOGRAM, HRZ, HTM, HTML, HTTP, HTTPS, ICB, ICO, ICON, IIQ, INFO, INLINE, IPL, ISOBRL, ISOBRL6, J2C, J2K, JBG, JBIG, JNG, JNX, JP2, JPC, JPE, JPEG, JPG, JPM, JPS, JPT, JSON, K25, KDC, LABEL, M2V, M4V, MAC, MAGICK, MAP, MASK, MAT, MATTE, MEF, MIFF, MKV, MNG, MONO, MOV, MP4, MPC, MPEG, MPG, MRW, MSL, MSVG, MTV, MVG, NEF, NRW, NULL, ORF, OTB, OTF, PAL, PALM, PAM, PANGO, PATTERN, PBM, PCD, PCDS, PCL, PCT, PCX, PDB, PDF, PDFA, PEF, PES, PFA, PFB, PFM, PGM, PGX, PICON, PICT, PIX, PJPEG, PLASMA, PNG, PNG00, PNG24, PNG32, PNG48, PNG64, PNG8, PNM, POCKETMOD, PPM, PREVIEW, PS, PS2, PS3, PSB, PSD, PTIF, PWP, RADIAL-GRADIENT, RAF, RAS, RAW, RGB, RGBA, RGBO, RGF, RLA, RLE, RMF, RW2, SCR, SCT, SFW, SGI, SHTML, SIX, SIXEL, SPARSE-COLOR, SR2, SRF, STEGANO, SUN, SVG, SVGZ, TEXT, TGA, THUMBNAIL, TIFF, TIFF64, TILE, TIM, TTC, TTF, TXT, UBRL, UBRL6, UIL, UYVY, VDA, VICAR, VID, VIDEO, VIFF, VIPS, VST, WBMP, WEBM, WEBP, WMF, WMV, WMZ, WPG, X, X3F, XBM, XC, XCF, XPM, XPS, XV, XWD, YCbCr, YCbCrA, YUV
image_format_transforms: image/heic → image/jpeg, image/heif → image/jpeg, image/heic-sequence → image/jpeg, image/heif-sequence → image/jpeg
gd_version: 2.3.3
gd_formats: GIF, JPEG, PNG, WebP, BMP, AVIF, XPM
ghostscript_version: 9.27

### wp-server ###

server_architecture: Linux 4.18.0-553.81.1.el8_10.x86_64 x86_64
httpd_software: Apache
php_version: 8.3.27 64bit
php_sapi: fpm-fcgi
max_input_variables: 5000
time_limit: 60
memory_limit: 512M
max_input_time: 60
upload_max_filesize: 32M
php_post_max_size: 32M
curl_version: 7.61.1 OpenSSL/1.1.1k
suhosin: false
imagick_availability: true
pretty_permalinks: true
htaccess_extra_rules: true
static_robotstxt_file: false
current: 2025-11-12T09:44:23+00:00
utc-time: Wednesday, 12-Nov-25 09:44:23 UTC
server-time: 2025-11-12T10:44:21+01:00

### wp-database ###

extension: mysqli
server_version: 8.0.43
client_version: mysqlnd 8.3.27
max_allowed_packet: 16777216
max_connections: 512

### wp-constants ###

WP_HOME: undefined
WP_SITEURL: undefined
WP_CONTENT_DIR: /srv/vhost/example.com/home/html/wp-content
WP_PLUGIN_DIR: /srv/vhost/example.com/home/html/wp-content/plugins
WP_MEMORY_LIMIT: 512M
WP_MAX_MEMORY_LIMIT: 512M
WP_DEBUG: false
WP_DEBUG_DISPLAY: true
WP_DEBUG_LOG: false
SCRIPT_DEBUG: false
WP_CACHE: false
CONCATENATE_SCRIPTS: undefined
COMPRESS_SCRIPTS: undefined
COMPRESS_CSS: undefined
WP_ENVIRONMENT_TYPE: undefined
WP_DEVELOPMENT_MODE: undefined
DB_CHARSET: utf8
DB_COLLATE: undefined

### wp-filesystem ###

wordpress: writable
wp-content: writable
uploads: writable
plugins: writable
themes: writable
fonts: does not exist


Attachments (1)

pcp-l10n-repro.zip​ (2.5 KB ) - added by nareknovembit 5 weeks ago.

Download all attachments as: .zip

Change History (3)

#1 @psykro
11 months ago

  • Milestone Awaiting Review
  • Resolution → invalid
  • Status new → closed

I just tested this on a clean WordPress install upgraded to 6.9 RC 1, using two different plugins (one being the same used in this ticket) and no errors were logged. This therefore might be an issue with the hosting service where the problem is happening.

#2 @nareknovembit
5 weeks ago

  • Resolution invalid
  • Status closed → reopened

Please consider reopening. This reproduces reliably on WordPress 7.0, and I believe the clean install test in comment:1 missed it for a specific reason rather than it being a hosting problem: the warning needs a plugin that calls
wp_get_installed_translations() late in the delete request. A clean install
has none, so it cannot fire there.

Attached is a minimal reproduction. The plugin in it is deliberately inert, a
plugin header and nothing else: no uninstall.php, no hooks, no code that loads
a text domain. The warning does not originate from the plugin being deleted.

Reproduction

  1. Copy pcp-l10n-repro/ into wp-content/plugins/
  2. Copy pcp-l10n-repro-fr_FR.l10n.php into wp-content/languages/plugins/, with no matching .po file
  3. Switch the site to a non-English locale, e.g. wp language core install fr_FR && wp site switch-language fr_FR
  4. Activate WooCommerce, or any plugin that calls wp_get_installed_translations() from pre_set_site_transient_update_plugins
  5. Set display_errors = On
  6. Delete the plugin from the Plugins screen

Backtrace

include(.../languages/plugins/pcp-l10n-repro-fr_FR.l10n.php):                                                                                                                           
Failed to open stream: No such file or directory in wp-includes/l10n.php on line 1682                                                                                                   
                                                                                                                                                                                        
#1  include                                   (l10n.php:1682)                                                                                                                           
#2  wp_get_l10n_php_file_data                 (l10n.php:1642)                                                                                                                           
#3  wp_get_installed_translations             (class-wc-helper-updater.php:502)                                                                                                         
#4  WC_Helper_Updater::get_translations_update_data                                                                                                                                     
#5  WC_Helper_Updater::transient_update_plugins                                                                                                                                         
#6  apply_filters( 'pre_set_site_transient_update_plugins' )                                                                                                                            
#7  set_site_transient                        (plugin.php:1042)                                                                                                                         
#8  delete_plugins                            (ajax-actions.php:4781)                                                                                                                   
#9  wp_ajax_delete_plugin                                                                                                                                                               


Cause

delete_plugins() removes the plugin's .po, .mo and .l10n.php files, then

calls set_site_transient( 'update_plugins', ... ) further down the same
function. A callback on that transient calls
wp_get_installed_translations( 'plugins' ), which reads
WP_Textdomain_Registry::get_language_files_from_path(). That glob() result is
cached in the translation_files cache group, so the list still contains the
files deleted moments earlier, and wp_get_l10n_php_file_data() includes each
path with no existence check

Core already has invalidation for this cache in
WP_Textdomain_Registry::invalidate_mo_files_cache(), but it is only wired to
upgrader_process_complete, so a plugin deletion never clears it.

On the user-visible symptom

With display_errors On the warnings are written into the admin-ajax.php
response ahead of the JSON, which breaks parsing and produces the "Deletion
failed" message even though the plugin is removed correctly. With
display_errors Off the deletion looks clean and the warnings only reach the
error log. That is most likely why this did not show up in testing.

Suggested fix

Guard the include, matching what WP_Translation_File::create() already does at
class-wp-translation-file.php:85:

function wp_get_l10n_php_file_data( $php_file ) {                                                                                                                                       
      if ( ! is_readable( $php_file ) ) {                                                                                                                                               
              return array();                                                                                                                                                           
      }                                                                                                                                                                                 
      $data = (array) include $php_file;                                                                                                                                                


Alternatively, or additionally, invalidate the translation_files cache in
delete_plugins() after removing the language files. The guard seems worth
having either way, since wp_get_l10n_php_file_data() is reachable from any
caller holding a stale path.

Note: See TracTickets for help on using tickets.