« Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of « text/plain » » : voici l’erreur remontée par Sentry, capturée automatiquement sur plusieurs sessions utilisateur, deux jours après avoir migré le script de vue d’un bloc vers la nouvelle API de Script Modules. Cette API, introduite par WordPress 6.5 alors en phase de release candidate à la date de cet article, permet d’enregistrer un module ES natif via wp_register_script_module() plutôt qu’un script classique.
Le test avait été mené volontairement sur un environnement de recette pointant vers la RC de WordPress 6.5, dans l’optique d’anticiper la bascule avant sa sortie stable début avril. C’est précisément ce choix d’anticipation qui a permis de repérer ce problème avant qu’il n’atteigne la production, une fois la version stable installée.
Comprendre la déclaration du module
Le fichier block.json du bloc déclarait le nouveau champ viewScriptModule, remplaçant l’ancien viewScript pour ce bloc précis :
{
"apiVersion": 3,
"name": "acme/bloc-interactif",
"viewScriptModule": "file:./view.js"
}
Ce champ s’appuie sur wp_register_script_module(), qui enregistre le fichier comme un module ES natif du navigateur, chargé avec l’attribut type="module", plutôt que comme un script classique. Cette distinction a une conséquence directe sur le serveur : un module ES doit impérativement être servi avec un type MIME JavaScript strict (text/javascript ou application/javascript), faute de quoi le navigateur refuse purement et simplement de l’exécuter.
La cause : une règle de serveur trop générique
L’investigation a mené vers la configuration Nginx de l’environnement de recette, qui servait l’ensemble des fichiers du dossier blocks/ avec une règle de type MIME générique héritée d’une configuration ancienne, forçant text/plain pour tout fichier sans extension reconnue explicitement par cette règle — une configuration écrite avant l’existence des Script Modules, jamais mise à jour depuis.

location /wp-content/themes/acme/blocks/ {
types {
text/plain js;
}
default_type text/plain;
}
Cette règle, invisible tant que les scripts étaient chargés en mode classique (le navigateur tolère un type MIME approximatif pour un script non-module), est devenue bloquante dès le passage à type="module", les navigateurs appliquant une vérification stricte du type MIME pour les modules ES, précisément pour des raisons de sécurité.
Le correctif de configuration serveur
location /wp-content/themes/acme/blocks/ {
types {
text/javascript js mjs;
text/css css;
}
}
Après correction de la configuration Nginx et purge du cache du CDN placé devant, l’erreur a immédiatement disparu des sessions suivantes remontées par Sentry.
Configurer Sentry pour ce type d’erreur
Sentry, déjà en place pour capturer les erreurs JavaScript côté client via le SDK @sentry/browser, a effectivement capturé l’erreur de chargement de module, mais son message brut ne suffisait pas à lui seul à identifier la cause serveur : il fallait croiser cette information avec l’onglet réseau des outils de développement pour repérer le type MIME fautif. Deux ajustements ont amélioré la lisibilité du signal :
- Ajout d’un contexte personnalisé (
Sentry.setContext()) précisant la version de WordPress et le mode de chargement du script concerné, pour distinguer rapidement les modules des scripts classiques dans le tableau de bord Sentry. - Configuration d’une alerte dédiée sur le motif de message « Failed to load module script », rare mais toujours révélateur d’un problème de configuration serveur plutôt que de logique applicative.
- Vérification systématique, après toute migration vers
viewScriptModule, du type MIME réellement renvoyé par le serveur avec une simple requêtecurl -Isur le fichier concerné.
Avant toute bascule vers les Script Modules, je vérifie systématiquement la configuration MIME du serveur cible : c’est un point qui n’a historiquement jamais eu besoin d’être strict avec les scripts classiques, et qui devient soudain bloquant avec les modules ES.
En résumé
Cette erreur n’avait rien à voir avec le code du bloc lui-même, entièrement correct, ni avec WordPress 6.5, dont l’implémentation des Script Modules respecte scrupuleusement les standards du navigateur. Elle provenait d’une configuration serveur écrite avant l’existence de cette API, jamais mise à jour. Tester sur une release candidate, plutôt que d’attendre la sortie stable, a permis de corriger ce point avant qu’il n’affecte le moindre visiteur en production.