Il est 9h12, le marketing veut un nouveau plugin de formulaire « tout de suite », et WordPress répond calmement : « Destination folder already exists. » Le site s’affiche toujours, mais l’administration est coincée, les installations échouent, et chaque « solution » trouvée en ligne consiste à supprimer quelque chose sous wp-content comme si c’était une serviette jetable.
Ne faites pas ça. wp-content contient les choses importantes de votre activité. Le maltraiter et vous passerez le reste de la journée à vous excuser auprès des personnes qui ont des réunions.
Ce que l’erreur signifie réellement (et ce qu’elle ne signifie pas)
WordPress lance « Destination folder already exists » lorsqu’il tente d’installer ou de mettre à jour un plugin/thème et découvre que le nom du répertoire cible est déjà présent. Cela semble évident, mais le détail clé est le suivant : WordPress ne peut pas déterminer avec confiance si le répertoire existant est une installation saine, une extraction partielle, un résidu d’une mise à jour échouée, ou quelque chose qu’il ne devrait pas toucher.
Donc WordPress joue la sécurité et abandonne. C’est généralement le bon choix. Votre travail consiste à déterminer si vous êtes face à :
- Un plugin/thème légitime déjà installé (vous essayez d’« installer » quelque chose qui est déjà là).
- Une installation partielle (le répertoire existe mais des fichiers manquent ; WordPress refuse d’écraser).
- Un problème de permissions/possession (WordPress ne peut pas supprimer/remplacer le répertoire, il le signale donc comme « existant » même s’il doit le remplacer).
- Un décalage de structure ZIP (l’archive s’extrait dans un nom de dossier qui entre en collision avec un dossier existant).
- Un décalage d’abstraction du système de fichiers (écritures directes vs méthodes FTP/SSH, répertoires temporaires, et manipulations d’umask).
Ce que ce n’est généralement pas :
- Une situation « WordPress est cassé » nécessitant la réinstallation complète du site.
- Une raison de supprimer des dossiers aléatoires sous
wp-contentjusqu’à ce que l’écran change. - Une raison de faire un chmod 777 partout (sauf si votre modèle de menace est « j’aime vivre dangereusement »).
Une citation à garder près de votre terminal : « L’espoir n’est pas une stratégie. » — Gen. Gordon R. Sullivan. Les erreurs WordPress incitent souvent à des opérations basées sur l’espoir. N’en faites pas.
Mode d’intervention rapide (vérifiez ceci en premier)
Si vous êtes chronométré, ne vous égarez pas. Traitez cela comme une triage d’incident.
Première étape : confirmez ce que WordPress essaie d’installer
- Plugin ou thème ? Quel nom/slug ? Depuis le dépôt ou un ZIP uploadé ?
- S’agit-il d’une installation, d’une mise à jour, ou d’une tentative de « réinstallation » ?
- Une mise à jour précédente a-t-elle échoué (écran blanc, « mode maintenance », notification bloquée) ?
Deuxième étape : inspectez le répertoire de destination
- Est-ce que
wp-content/plugins/<slug>ouwp-content/themes/<slug>existe ? - Est-ce un arbre complet ou un désordre à moitié extrait ?
- Y a-t-il des voisins étranges comme
<slug>.tmp,<slug>_old, ou un répertoireupgradelaissé derrière ?
Troisième étape : vérifiez la possession, les permissions et le chemin d’écriture
- Le user du serveur web peut-il écrire dans
wp-contentetwp-content/upgrade? - WordPress utilise-t-il un accès direct au système de fichiers ou demande-t-il des identifiants FTP ?
- Le disque est-il plein ou en pénurie d’inodes ?
Quatrième étape : consultez les logs (vraiment)
- Log d’erreur du serveur web : permission refusée, erreurs d’unzip, timeouts.
- Log PHP-FPM : restrictions open_basedir, problèmes de répertoire temporaire.
- Log de debug WordPress : messages liés au système de fichiers, à la mise à niveau et à l’extraction.
Cinquième étape : décidez du chemin le plus sûr
- Si le répertoire contient une installation valide : faites une mise à jour, pas une installation ; ou utilisez WP-CLI avec
--forceavec précaution. - S’il est partiel : renommez-le, puis réinstallez proprement.
- S’il s’agit d’un problème de permissions : corrigez la possession/ACLs, puis relancez la mise à jour.
- S’il s’agit d’une structure ZIP : reconditionnez ou choisissez un autre ZIP.
Faits intéressants et contexte historique (pour comprendre l’étrangeté)
Six à dix faits rapides qui expliquent pourquoi cette erreur existe et pourquoi elle est toujours pénible :
- WordPress partait de l’hypothèse d’un hébergement mutualisé, où « installer un plugin » signifiait « uploader via FTP », pas remplacer des répertoires de façon atomique.
- Le système de mise à niveau repose sur un espace de travail temporaire (souvent
wp-content/upgrade) et une séquence de copie/rename ; quand cette séquence est interrompue, des restes sont fréquents. - Les archives ZIP ne sont pas standardisées par bonté : de nombreux fournisseurs livrent un ZIP avec un dossier racine qui ne correspond pas au slug du plugin, provoquant collisions ou dossiers imbriqués.
- Sur certains hébergeurs, PHP s’exécute sous un utilisateur différent du serveur web (ou sous un utilisateur de pool), générant une possession mixte qui ne pose problème qu’au moment des écritures.
- La logique du « filesystem method » existe parce que WordPress ne peut pas supposer l’accès en écriture ; il essaie les écritures directes, puis bascule sur FTP/SSH quand c’est bloqué.
- Le remplacement atomique de répertoire n’est pas toujours disponible sur tous les systèmes de fichiers/modèles de permissions ; WordPress joue la prudence pour éviter de supprimer du code fonctionnel.
- Les répertoires de plugins et thèmes sont devenus des gestionnaires de paquets informels bien avant que WordPress ait des sémantiques de rollback/transaction fiables.
- Le « mode maintenance » coincé vient d’un seul fichier (
.maintenance) créé durant les mises à niveau ; si le nettoyage échoue, le site peut sembler « hors service » alors qu’il fonctionne.
Modes d’échec : les vraies raisons pour lesquelles le dossier « existe déjà »
1) Le plugin/thème est déjà installé, mais vous tentez une « installation »
Le plus courant avec les uploads ZIP et les packages fournis par des vendeurs. Le dossier existe, WordPress le voit, refuse d’écraser et renvoie le message net. La bonne action est soit de mettre à jour depuis la page Plugins, soit de supprimer/renommer le répertoire existant après vérification qu’il est sûr.
2) Extraction partielle d’un échec précédent
Un timeout en plein dézippage, un disque plein, un worker PHP tué, un timeout de reverse proxy, ou un ingénieur ops qui a redémarré PHP-FPM « pour nettoyer ». Vous vous retrouvez avec un répertoire existant mais incomplet. WordPress ne peut pas décider s’il doit l’effacer, donc il s’arrête.
3) Incompatibilité de permissions/possession
Le dossier existe et devrait pouvoir être remplacé, mais le processus web ne peut pas le supprimer. WordPress signale « déjà existant » parce que sa tentative de nettoyage échoue et il ne peut pas poursuivre en sécurité. Cherchez Permission denied dans les logs. Vous le trouverez.
4) SELinux/AppArmor ou politiques durcies
Sur des systèmes durcis, les droits semblent corrects mais le contrôle d’accès obligatoire bloque les écritures. Cela apparaît comme des erreurs de permission même avec des bits de mode « corrects ».
5) open_basedir ou problèmes de répertoire temporaire
L’extraction utilise souvent un répertoire temporaire ; si PHP ne peut pas y écrire, vous obtenez des installations à moitié finies et des erreurs déroutantes. WordPress rencontre alors le dossier de destination à moitié créé et s’arrête.
6) Comportement étrange du système de fichiers (NFS, CIFS, volumes objets)
Les systèmes de fichiers réseau peuvent avoir du caching, une visibilité retardée, ou des sémantiques de rename qui ne se comportent pas comme un ext4/XFS local. Le remplacement de répertoire peut échouer de façons qui ressemblent à « existe » mais signifient en réalité « le rename n’a pas propagé ».
7) Outils de déploiement qui combattent WordPress
Si vous déployez du code via Git, rsync ou containers, et que WordPress tente de s’auto-modifier, vous avez des sources de vérité en duel. L’erreur est WordPress qui aboie à une porte verrouillée.
Blague #1 : Les mises à jour WordPress, c’est comme les chaises de bureau — généralement OK, jusqu’à ce que quelqu’un s’appuie en arrière avec confiance.
Tâches pratiques : commandes, sorties et décisions à prendre
Voici des tâches réelles que vous pouvez exécuter sur un hôte Linux. Chacune inclut : la commande, ce qu’une sortie typique signifie, et la décision à prendre.
Tâche 1 : Confirmer que le répertoire cible existe
cr0x@server:~$ sudo ls -ld /var/www/html/wp-content/plugins/contact-form-7
drwxr-xr-x 5 root root 4096 Dec 27 08:41 /var/www/html/wp-content/plugins/contact-form-7
Sens : Le répertoire du plugin existe et est possédé par root:root. C’est suspect sur une machine WordPress typique où le processus web a besoin d’accès en écriture.
Décision : Ne le supprimez pas encore. Vérifiez ensuite s’il s’agit d’une installation valide et si la possession est incorrecte.
Tâche 2 : Vérifier si c’est une installation partielle (nombre de fichiers et fichiers attendus)
cr0x@server:~$ sudo find /var/www/html/wp-content/plugins/contact-form-7 -maxdepth 2 -type f | wc -l
3
Sens : Trois fichiers seulement est probablement incomplet pour un vrai plugin. Un plugin sain a généralement des dizaines de fichiers.
Décision : Traiter comme extraction partielle. Prévoir de renommer et réinstaller.
Tâche 3 : Identifier l’utilisateur du serveur web (Apache)
cr0x@server:~$ ps -eo user,comm | egrep 'apache2|httpd' | head
www-data apache2
www-data apache2
www-data apache2
Sens : Les processus Apache tournent en www-data.
Décision : Les répertoires de plugins/thèmes devraient généralement être écrits par www-data (ou gérés par l’outil de déploiement, mais dans ce cas WordPress ne devrait pas s’auto-mettre à jour).
Tâche 4 : Identifier l’utilisateur du pool PHP-FPM (Nginx + PHP-FPM)
cr0x@server:~$ sudo grep -R "^\s*user\s*=" /etc/php/*/fpm/pool.d/www.conf | head -n 1
user = www-data
Sens : PHP s’exécute aussi en www-data (bien ; moins de surprises de possession mixte).
Décision : Si vous avez encore des dossiers possédés par root, ils ont probablement été créés par des actions manuelles (unzip en root, rsync en root, ou une étape CI mal configurée).
Tâche 5 : Tester l’accès en écriture à wp-content (en tant qu’utilisateur web)
cr0x@server:~$ sudo -u www-data bash -lc 'touch /var/www/html/wp-content/.write-test && echo ok && rm /var/www/html/wp-content/.write-test'
ok
Sens : L’utilisateur web peut écrire dans wp-content au niveau supérieur.
Décision : L’échec est plus probablement à l’intérieur d’un sous-répertoire spécifique (plugins/themes/upgrade) ou dû à la possession/ACL sur ce dossier de plugin.
Tâche 6 : Vérifier la santé du répertoire temp d’upgrade
cr0x@server:~$ sudo -u www-data bash -lc 'mkdir -p /var/www/html/wp-content/upgrade && touch /var/www/html/wp-content/upgrade/.upgrade-test && ls -la /var/www/html/wp-content/upgrade | head'
total 8
drwxr-xr-x 2 www-data www-data 4096 Dec 27 09:01 .
drwxr-xr-x 10 www-data www-data 4096 Dec 27 09:01 ..
-rw-r--r-- 1 www-data www-data 0 Dec 27 09:01 .upgrade-test
Sens : WordPress peut préparer les fichiers d’upgrade.
Décision : Si les upgrades échouent toujours, concentrez-vous sur la possession du dossier de destination, l’espace disque, ou la structure du ZIP.
Tâche 7 : Vérifier l’espace disque et la pression des inodes
cr0x@server:~$ df -h /var/www/html
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 40G 39G 600M 99% /
Sens : Vous êtes presque à court d’espace. L’extraction peut échouer en cours et laisser le dossier de destination derrière.
Décision : Libérez d’abord de l’espace. Puis nettoyez les répertoires partiels et réessayez. Si vous ignorez ça, vous continuerez à générer des installations incomplètes.
cr0x@server:~$ df -i /var/www/html
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 2621440 2619000 2440 100% /
Sens : Les inodes sont épuisés. Cela peut casser les installations même si « l’espace disque » semble correct ailleurs.
Décision : Trouvez et supprimez les fichiers générant beaucoup d’inodes (répertoires de cache, anciennes sauvegardes). Puis réessayez.
Tâche 8 : Rechercher un mode maintenance bloqué
cr0x@server:~$ sudo ls -la /var/www/html/.maintenance
-rw-r--r-- 1 www-data www-data 57 Dec 27 08:40 /var/www/html/.maintenance
Sens : Une mise à jour précédente ne s’est pas nettoyée. Les utilisateurs peuvent voir « Briefly unavailable for scheduled maintenance. »
Décision : Si aucune mise à jour n’est active, supprimez-le après avoir vérifié qu’aucun processus d’update n’est en cours. Ensuite, concentrez-vous sur la cause de l’échec.
Tâche 9 : Vérifier le log de debug WordPress pour des erreurs d’extraction/FS
cr0x@server:~$ sudo tail -n 30 /var/www/html/wp-content/debug.log
[27-Dec-2025 08:41:12 UTC] PHP Warning: copy(/var/www/html/wp-content/plugins/contact-form-7/readme.txt): failed to open stream: Permission denied
[27-Dec-2025 08:41:12 UTC] PHP Warning: unlink(/var/www/html/wp-content/plugins/contact-form-7/readme.txt): Permission denied
Sens : Problème classique de possession/permissions à l’intérieur du répertoire de destination.
Décision : Corrigez la possession/mode/ACL sur ce chemin. Ne relancez pas sans cesse l’installation ; vous n’obtiendrez que des logs et de la frustration.
Tâche 10 : Inspecter les logs du serveur web / PHP pour l’erreur réelle
cr0x@server:~$ sudo tail -n 50 /var/log/nginx/error.log
2025/12/27 08:41:12 [error] 12345#12345: *901 FastCGI sent in stderr: "PHP message: PHP Warning: mkdir(): Permission denied in /var/www/html/wp-admin/includes/class-wp-filesystem-direct.php on line 56" while reading response header from upstream
Sens : La méthode de système de fichiers est directe, mais mkdir a échoué. Ce n’est pas un problème de logique WordPress ; c’est un problème d’accès au niveau OS.
Décision : Corrigez les permissions/possession, puis relancez. Évitez les uploads ZIP bricolés tant que le chemin d’écriture sous-jacent ne fonctionne pas.
Tâche 11 : Valider la structure ZIP avant l’upload (problème fréquent chez les fournisseurs)
cr0x@server:~$ unzip -l /tmp/vendor-plugin.zip | head
Archive: /tmp/vendor-plugin.zip
Length Date Time Name
--------- ---------- ----- ----
0 2025-12-01 10:10 vendor-plugin/
0 2025-12-01 10:10 vendor-plugin/vendor-plugin/
2345 2025-12-01 10:10 vendor-plugin/vendor-plugin/plugin.php
Sens : Dossier imbriqué : vendor-plugin/vendor-plugin/. WordPress extraira dans vendor-plugin et le vrai plugin se trouve un niveau plus bas, ce qui crée de la confusion et peut entrer en collision avec des répertoires existants.
Décision : Reconditionnez le ZIP (le dossier de niveau supérieur doit être le slug du plugin et contenir directement les fichiers du plugin) ou installez via WP-CLI depuis un chemin corrigé.
Tâche 12 : Utiliser WP-CLI pour voir ce que WordPress considère comme installé
cr0x@server:~$ cd /var/www/html && sudo -u www-data wp plugin list --field=name | head
akismet
contact-form-7
hello
Sens : WordPress reconnaît déjà contact-form-7 comme installé. La tentative d’installation depuis l’admin est redondante ou le répertoire est corrompu mais toujours détecté.
Décision : Envisagez une réinstallation contrôlée via WP-CLI (avec overwrite), ou renommez le répertoire et réinstallez proprement.
Tâche 13 : Réinstallation contrôlée avec WP-CLI (plugin) sans suppression aveugle
cr0x@server:~$ cd /var/www/html && sudo -u www-data wp plugin install contact-form-7 --force
Installing Contact Form 7 (6.0.3)
Downloading installation package from https://downloads.wordpress.org/plugin/contact-form-7.6.0.3.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.
Sens : --force indique à WP-CLI d’écraser. C’est plus sûr que rm -rf manuel si vous faites confiance à la source et que vos permissions de système de fichiers sont correctes.
Décision : Si cela fonctionne, votre problème était probablement le refus d’écrasement de l’admin WordPress pendant que WP-CLI l’a géré. Si cela échoue, lisez l’erreur exacte et corrigez la cause au niveau OS.
Tâche 14 : Renommer le dossier de destination pour conserver les preuves
cr0x@server:~$ sudo mv /var/www/html/wp-content/plugins/contact-form-7 /var/www/html/wp-content/plugins/contact-form-7.broken-20251227
Sens : Vous avez supprimé la collision tout en conservant l’ancien répertoire pour l’analyse ou le rollback.
Décision : Réessayez l’installation. Si elle réussit, faites un diff de l’ancien dossier plus tard pour comprendre ce qui a échoué.
Tâche 15 : Corriger la possession proprement (et uniquement où nécessaire)
cr0x@server:~$ sudo chown -R www-data:www-data /var/www/html/wp-content/plugins/contact-form-7
Sens : L’utilisateur web peut maintenant gérer les fichiers à l’intérieur de ce répertoire de plugin.
Décision : Relancez la mise à jour/installation. Si vous avez une chaîne de déploiement qui attend une possession root, arrêtez-vous et décidez qui « possède » le code : WordPress ou votre pipeline. Choisissez l’un des deux.
Tâche 16 : Repérer et supprimer les attributs immuables récalcitrants (rare, mais épicé)
cr0x@server:~$ sudo lsattr -d /var/www/html/wp-content/plugins/contact-form-7
----i---------e------- /var/www/html/wp-content/plugins/contact-form-7
Sens : Le répertoire a l’attribut immuable (i). Les suppressions et écritures échoueront même en root.
Décision : Si cela a été défini par durcissement ou un outil de sauvegarde, retirez l’immuabilité avant d’essayer de mettre à jour.
cr0x@server:~$ sudo chattr -i /var/www/html/wp-content/plugins/contact-form-7
Tâche 17 : Vérifier le contexte SELinux (si applicable)
cr0x@server:~$ sudo getenforce
Enforcing
cr0x@server:~$ sudo ls -Zd /var/www/html/wp-content
unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/wp-content
Sens : Si wp-content est étiqueté httpd_sys_content_t, Apache peut lire mais pas nécessairement écrire. Les chemins modifiables doivent typiquement être en httpd_sys_rw_content_t selon la politique.
Décision : Ajustez les contextes uniquement pour les répertoires écrits, pas pour tout le docroot.
Tâche 18 : Trouver les répertoires de travail d’upgrade restants
cr0x@server:~$ sudo find /var/www/html/wp-content/upgrade -maxdepth 1 -type d -printf '%f\n' | head
upgrade
tmp-1234567890
tmp-1735290072
Sens : Les anciens répertoires temporaires peuvent indiquer des mises à jour échouées.
Décision : Si aucune mise à jour n’est en cours, supprimez les anciens répertoires temp pour réduire les collisions et l’encombrement, mais conservez le dernier si vous avez besoin de preuves.
Blague #2 : Rien ne dit « niveau entreprise » comme un dossier nommé tmp-1735290072 qui contrôle tranquillement votre week-end.
Corrections sûres qui n’abîment pas wp-content
Stratégie A : Renommer, ne pas supprimer (déplacement par défaut)
Quand vous n’êtes pas sûr à 100 % du contenu du répertoire, renommez-le. Renommer est réversible, rapide et conserve les preuves. La seule fois où vous devriez supprimer immédiatement c’est lorsque vous nettoyez un dossier de cache connu ou un répertoire temporaire sûr.
Bon : contact-form-7.broken-20251227
Mauvais : rm -rf contact-form-7 puis « je crois que c’était OK ? »
Stratégie B : Utiliser WP-CLI pour un écrasement contrôlé
L’interface d’administration WordPress est conservatrice. WP-CLI est directe. Si vous êtes ops, vous voulez un comportement déterministe et des logs capturables. Utilisez :
wp plugin install <slug> --forcewp theme install <slug> --force
Mais seulement après avoir confirmé que les permissions d’écriture sous-jacentes sont correctes. Si les permissions sont incorrectes, forcer les écritures ne fera que produire un échec plus bruyant.
Stratégie C : Corriger la possession/permissions avec retenue
La « solution » la plus dangereuse dans l’écosystème WordPress est de changer les permissions massivement :
- Évitez :
chmod -R 777n’importe où. - Évitez : chown récursif de tout le docroot si vous déployez via Git/CI ; vous créez de la dérive et surprendrez votre prochain déploiement.
Faites plutôt : gardez le code core possédé par votre user de déploiement (ou root), et rendez seulement certains répertoires écrits accessibles à l’utilisateur d’exécution :
wp-content/uploadswp-content/cache(si utilisé)wp-content/upgrade- les répertoires de plugins/thèmes uniquement si vous autorisez les mises à jour in-place
Stratégie D : Empêchez WordPress de s’auto-modifier en production (si possible)
Prise de position : sur des systèmes de production sérieux, WordPress ne devrait pas pouvoir se mettre à jour via des clics dans wp-admin. Ce n’est pas du purisme ; c’est réduire les changements incontrôlés. Si vous avez CI/CD, images immuables, ou au moins un contrôle des changements, gérez les mises à jour via le déploiement. Alors cette classe d’erreurs diminue fortement.
Compromis : vous devez arrêter de considérer WordPress comme un gestionnaire de paquets. Choisissez un modèle :
- Modèle 1 : WordPress gère plugins/thèmes. Vous assurez les permissions et acceptez le risque des changements admin.
- Modèle 2 : Votre pipeline gère plugins/thèmes. Vous désactivez les modifications de fichiers dans WordPress et déployez des artefacts.
Stratégie E : Réparer une installation cassée sans perdre les personnalisations
Certains plugins stockent la configuration dans la base de données, pas dans le système de fichiers. Si le répertoire du plugin est corrompu, vous pouvez généralement réinstaller les fichiers du plugin sans perdre les réglages. Mais « généralement » n’est pas « toujours ».
Approche sûre :
- Sauvegardez la base de données (ou prenez un snapshot).
- Sauvegardez le répertoire du plugin en le renommant.
- Réinstallez le plugin/thème proprement.
- Vérifiez le comportement et les logs.
- Supprimez l’ancien répertoire seulement ensuite.
Trois mini-récits d’entreprise (comment ça tourne mal en vrai)
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne exécutait WordPress derrière Nginx et PHP-FPM sur une VM. Le marketing avait un accès admin et installait des plugins directement. L’équipe ops supposait que parce que le site tournait depuis des mois, les permissions de fichiers étaient « correctes ». Elles ne l’étaient pas — simplement non testées.
Un matin, une mise à jour de plugin a échoué pendant l’extraction. Le répertoire du plugin existait, partiellement mis à jour, et WordPress refusait de réinstaller : « Destination folder already exists. » Le panneau admin continuait d’afficher des mises à jour qui ne pouvaient pas se terminer. Personne n’a vérifié l’espace disque ou la possession ; ils ont simplement relancé la mise à jour cinq fois, créant plus de répertoires temporaires partiels.
L’hypothèse erronée était subtile : « Si WordPress peut lire les plugins, il peut les mettre à jour. » Sur cet hôte, une fenêtre de maintenance précédente avait copié des répertoires de plugins en root depuis une archive de sauvegarde. WordPress pouvait les lire. Il ne pouvait pas les remplacer.
La correction fut ennuyeuse : chown uniquement le répertoire de plugin affecté et le répertoire de staging d’upgrade vers l’utilisateur PHP-FPM, puis réinstaller avec WP-CLI. Le postmortem a recommandé de verrouiller les mises à jour en production et de déplacer la gestion des plugins dans le pipeline de déploiement.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une organisation a déplacé wp-content sur un système de fichiers réseau pour que plusieurs nœuds web partagent les uploads et les plugins. Ça semblait propre : une source de vérité, plus besoin de rsync des médias, scalabilité plus facile. L’optimisation était « le stockage partagé résout tout », une phrase qui vieillit mal.
Pendant une mise à jour de plugin, WordPress a extrait le ZIP sur le nœud A, mais le nœud B voyait encore l’état ancien du répertoire à cause du cache et d’une propagation retardée des métadonnées. WordPress sur le nœud B a tenté la mise à jour aussi (oui, les gens cliquent deux fois), est entré en collision avec un répertoire dans un état intermédiaire, et a lancé « Destination folder already exists. » Le plugin s’est retrouvé à moitié neuf, à moitié ancien entre les nœuds.
Ils ont essayé de « corriger » en augmentant les timeouts PHP et en ajoutant des retries. Ça a élargi la zone d’impact. Finalement, ils ont dû rendre l’interface admin en lecture seule pendant les déploiements et s’assurer qu’un seul nœud effectuait les upgrades — ou mieux, arrêter les upgrades via wp-admin entièrement.
La leçon n’était pas « ne jamais utiliser NFS ». C’était : si votre application attend des sémantiques locales pour le remplacement atomique de répertoires, concevez autour. Soit centralisez les mises à jour via un job de déploiement unique, soit empaquetez plugins/thèmes comme artefacts et déployez-les de façon prévisible.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une grande instance interne WordPress servait de documentation. Ce n’était pas glamour, mais c’était critique. L’équipe appliquait deux pratiques : (1) chaque changement passait par un pipeline de déploiement, et (2) le système de fichiers était majoritairement en lecture seule pour l’utilisateur runtime sauf uploads et une petite liste de répertoires écrits.
Un après-midi, un fournisseur a livré un ZIP de « hotfix plugin » et a insisté pour l’installer immédiatement. L’admin a essayé. WordPress ne pouvait pas écrire dans le répertoire des plugins (par conception), et l’erreur dans l’UI était — prévisible — une variante de conflit de dossier de destination / impossibilité d’écraser. La panique a duré environ cinq minutes.
Parce que la pratique était ennuyeuse et cohérente, la réponse a été simple : l’équipe a mis en scène le plugin dans un workspace de build, validé la structure ZIP, le reconditionné correctement, scanné, puis déployé comme n’importe quel autre changement de code. Pas de chmod manuel. Pas d’archéologie nocturne dans wp-content.
Le site est resté stable, et « l’urgence » est devenue un changement normal avec auditabilité. C’est étonnant comme « on fait les déploiements de la même manière à chaque fois » fait souvent la différence entre une petite gêne et une grosse panne.
Erreurs courantes : symptôme → cause → correctif
1) Symptom : « Destination folder already exists » lors de l’upload d’un ZIP
Cause : Le répertoire du plugin/thème existe déjà d’une installation précédente, ou le ZIP contient un dossier de premier niveau qui correspond à un dossier existant.
Correctif : Vérifiez le chemin de destination. Renommez le répertoire existant. Validez la structure du ZIP avec unzip -l. Reconditionnez s’il est imbriqué.
2) Symptom : L’erreur se répète après avoir « supprimé le plugin » dans wp-admin
Cause : La suppression dans l’UI a échoué pour des raisons de permissions ; le répertoire est resté sur le disque. L’UI WordPress n’est pas une promesse, c’est une requête.
Correctif : Vérifiez la présence et la possession du répertoire sur le disque. Supprimez/renommez sur le disque une fois que vous avez confirmé que c’est sûr.
3) Symptom : La mise à jour échoue ; le répertoire du plugin existe mais le plugin est cassé
Cause : Extraction partielle due à un timeout, disque plein, épuisement d’inodes, ou worker PHP tué.
Correctif : Libérez espace/inodes, nettoyez les répertoires temporaires d’upgrade, renommez le répertoire de plugin cassé, réinstallez avec WP-CLI.
4) Symptom : WordPress demande des identifiants FTP de manière inattendue
Cause : WordPress ne peut pas écrire directement sur le système de fichiers, il bascule donc sur la méthode filesystem FTP.
Correctif : Corrigez les permissions/possession pour l’utilisateur runtime sur les répertoires nécessaires, ou adoptez un workflow FTP/SSH (non recommandé pour la production).
5) Symptom : Les permissions semblent correctes mais les mises à jour échouent toujours
Cause : SELinux/AppArmor bloque les écritures ; ou restrictions open_basedir/répertoire temp.
Correctif : Vérifiez le mode d’enforcement et les contextes ; vérifiez le répertoire temp PHP ; ajustez la politique pour des chemins écrits spécifiques.
6) Symptom : Ça marche sur un nœud, échoue sur un autre (multi-nœud)
Cause : Problèmes de cohérence du stockage partagé ou différences d’utilisateurs runtime/UID entre nœuds.
Correctif : Assurez une UID/GID cohérente, centralisez les mises à jour, évitez les mises à jour via wp-admin dans les environnements en cluster.
7) Symptom : Un seul plugin/thème échoue ; les autres se mettent à jour correctement
Cause : Ce répertoire a une possession, ACL, attribut immuable ou contenu corrompu différent.
Correctif : Inspectez ce chemin spécifique avec ls -l, getfacl, lsattr. Réparez de façon ciblée.
8) Symptom : « Could not create directory » pendant l’installation, puis « destination folder already exists » au second essai
Cause : La première tentative a créé le répertoire mais n’a pas pu le remplir ; la deuxième tentative entre en collision avec le dossier vide laissé derrière.
Correctif : Supprimez/renommez le dossier vide, corrigez la cause initiale (permissions/disque/temp), puis réinstallez.
Listes de contrôle / plan pas à pas (raisonnable, reproductible)
Checklist 1 : WordPress sur serveur unique où les installations via wp-admin sont autorisées
- Identifiez le slug du plugin/thème que vous installez (nom de dossier sous
wp-content). - Vérifiez l’existence : le répertoire existe-t-il déjà ?
- Vérifiez la santé : s’agit-il d’une installation complète ou partielle ?
- Vérifiez le chemin d’écriture : l’utilisateur runtime peut-il écrire dans
wp-contentetwp-content/upgrade? - Vérifiez disque/inodes : ne dépannez pas des installations sur un système de fichiers plein.
- Renommez le répertoire si vous suspectez une extraction partielle ou une corruption.
- Réinstallez avec WP-CLI si possible (c’est plus clair et scriptable).
- Vérifiez : le plugin s’active, les logs sont propres, le site se rend correctement.
- Nettoyez : supprimez l’ancien répertoire renommé après une période de validation.
Checklist 2 : Système de production avec CI/CD (recommandé)
- Désactivez les modifications de fichiers en production (pour que wp-admin ne mute pas le code).
- Rendez le docroot en lecture seule pour l’utilisateur runtime sauf uploads et un ensemble contrôlé de répertoires.
- Générez des artefacts (plugins/thèmes figés à des versions) dans le CI, pas sur le serveur.
- Déployez de manière atomique (répertoires de release + swap de symlink) quand c’est possible.
- Préparez un plan de rollback et testez-le (oui, testez-le).
- Observez : centralisez la collecte des logs d’erreur, PHP-FPM et WordPress debug.
Pas à pas : récupération propre de « destination folder already exists » (plugin)
- Faites un snapshot du système de fichiers ou au moins une tarball rapide de
wp-content/plugins/<slug>et un dump de la base de données. - Confirmez que le répertoire existe et inspectez-le.
- Renommez
<slug>en<slug>.broken-<date>. - Assurez-vous que l’utilisateur runtime peut écrire dans
wp-contentetwp-content/upgrade. - Installez/réinstallez en utilisant WP-CLI avec
--force(ou installez via l’administration une fois la collision supprimée). - Activez le plugin et effectuez un test fonctionnel rapide (front-end, wp-admin, tâches cron éventuelles).
- Surveillez les logs pendant 10–15 minutes d’utilisation normale.
- Supprimez le répertoire renommé lorsque vous êtes confiant.
FAQ
1) « Destination folder already exists » signifie-t-il que le plugin est déjà installé ?
Parfois. Cela signifie seulement que le nom du répertoire existe. Cela peut être une installation saine, ou un répertoire à moitié extrait issu d’une mise à jour échouée. Confirmez avec wp plugin list et en inspectant le contenu du répertoire.
2) Dois-je supprimer le dossier sous wp-content/plugins pour régler le problème ?
Préférez renommer d’abord. La suppression est irréversible et détruit souvent des preuves nécessaires pour comprendre l’échec. Renommez, réinstallez, validez, puis supprimez plus tard.
3) Pourquoi WordPress refuse-t-il d’écraser le dossier ?
Parce qu’écraser peut être destructeur si le répertoire existant contient une version fonctionnelle ou des modifications locales. WordPress choisit la sécurité plutôt que la commodité, même quand c’est gênant.
4) WP-CLI peut-il résoudre cela plus rapidement que wp-admin ?
Oui. WP-CLI vous donne des options déterministes comme --force et des messages d’erreur plus clairs. Il évite aussi les timeouts navigateur/proxy pendant de grandes extractions.
5) Réinstaller un plugin effacera-t-il ses réglages ?
Habituellement, les réglages du plugin sont stockés en base de données et survivent à une réinstallation des fichiers. Mais certains plugins stockent des fichiers de config dans leur répertoire. Sauvegardez la base et le répertoire avant d’écraser quoi que ce soit.
6) J’ai corrigé les permissions, mais l’erreur persiste. Et maintenant ?
Vérifiez les refus SELinux/AppArmor, les attributs immuables, et l’épuisement disque/inodes. Validez aussi la structure ZIP — des dossiers imbriqués peuvent générer des collisions répétées.
7) Pourquoi cela arrive-t-il plus souvent en config clusterisée ?
Parce que le stockage partagé et plusieurs nœuds ajoutent des problèmes de cohérence et de concurrence. Deux nœuds essayant de « mettre à jour » le même répertoire, c’est comme obtenir du code à moitié neuf et des vérifications d’existence confuses.
8) Dois-je autoriser WordPress à mettre à jour plugins/thèmes en production ?
Si vous gérez un petit site sur un seul serveur et acceptez le risque, c’est possible avec de bonnes sauvegardes. Si vous gérez de la production avec contrôle des changements, utilisez CI/CD et limitez les écritures runtime.
9) Qu’en est-il du réglage WordPress FS_METHOD ?
Forcer FS_METHOD à direct peut masquer des problèmes de permissions sous-jacents dans certains contextes et aggraver la sécurité dans d’autres. Corrigez la possession/ACL sous-jacente et laissez WordPress choisir direct seulement quand c’est approprié.
Étapes suivantes que vous pouvez réellement faire aujourd’hui
L’erreur « Destination folder already exists » est le message de WordPress : « Je vois quelque chose sur le disque et je ne suis pas assez confiant pour l’écraser. » Traitez-la comme un verrou de sécurité, pas comme une insulte.
- Exécutez le mode d’intervention rapide : confirmez le slug, inspectez le répertoire de destination, vérifiez les permissions d’écriture, l’espace disque/inodes, puis consultez les logs.
- Renommez avant de supprimer. Conservez les preuves, réduisez le regret.
- Corrigez la cause (possession, répertoire de staging, SELinux, répertoires temporaires), pas seulement le symptôme.
- Choisissez un modèle opérationnel : soit WordPress gère plugins/thèmes (et vous le supportez), soit votre pipeline le fait (et WordPress cesse d’essayer).
- Rendez-le reproductible : transformez ces étapes en runbook et arrêtez d’apprendre la même leçon à 9h du matin.
Si vous ne faites rien d’autre : arrêtez de « réparer » WordPress en supprimant aléatoirement le contenu de wp-content. Ce n’est pas de l’exploitation. C’est du jeu avec un meilleur habillage.