Aller au contenu
Sylvain Joffraud l4w
Retour

Message d’erreur : une hypothèse, pas un diagnostic

Synthèse de plusieurs incidents récents qui ont un point commun gênant : le message d’erreur pointait dans la mauvaise direction. À chaque fois, croire le message a coûté du temps ; tester son contraire a débloqué la situation. Quatre cas, un même réflexe à acquérir.

Cas 1 — « Access denied for user @localhost »

Deux jours perdus à régénérer un mot de passe MariaDB qui était correct depuis le début. Le message disait « access denied » ; on a entendu « mauvais mot de passe ». Il fallait entendre « host effectif = localhost » : la connexion tombait sur le socket Unix, et le grant n’existait que sur 'user'@'%' (TCP). Un --protocol=TCP -h 127.0.0.1 et c’est passé du premier coup.

Le message disait qui est refusé (@localhost), pas pourquoi on l’a cru (mot de passe). L’info de diagnostic était dans le host, pas dans « denied ».

Cas 2 — « Numéro de téléphone international invalide »

Un numéro visuellement parfait, rejeté côté serveur sur un seul device. Le message accusait le format du numéro. La vraie cause : iOS Safari injectait une espace insécable (U+00A0) que la regex \s du serveur ne matchait pas. Le numéro était valide ; c’est un octet invisible qui ne l’était pas.

error_log(bin2hex($phone));
// 2b3333 c2a0 363831...   ← c2a0 = U+00A0 (NBSP), pas un espace ASCII

Le message parlait du numéro. Les octets parlaient du séparateur. bin2hex a tranché en deux secondes ce que la lecture du message n’aurait jamais révélé.

Cas 3 — « Pièces manquantes » (flash global)

Celui-ci ne mentait pas… il était juste inutile. Sur un formulaire d’upload à huit champs, un flash global « il manque des pièces » est techniquement vrai mais opérationnellement creux : l’utilisateur doit deviner lesquelles. Le correctif n’a pas touché à la véracité du message, mais à sa granularité — une erreur inline, sous chaque champ concerné.

Un message vrai mais sans contexte n’est pas un diagnostic pour l’utilisateur non plus. La précision est l’information.

Cas 4 — « HTTP 500 sur l’endpoint de génération »

Un 500 sur l’appel à une API d’éditique externe. Le réflexe : « le template est cassé ». Faux. Le problème était un encodage au niveau du gateway en amont de l’API — la charge utile arrivait mal encodée avant d’atteindre le moteur de rendu. Le 500 désignait le dernier maillon visible, pas le maillon fautif.

Un code d’erreur désigne où ça casse, rarement pourquoi. Le 500 était au bout de la chaîne ; la cause était au début.

Le fil rouge

Dans les quatre cas, le message d’erreur était une hypothèse formulée par le système sur ce qui n’allait pas — pas un diagnostic vérifié. « Access denied » suppose un problème d’authentification. « Numéro invalide » suppose un problème de format. « 500 » suppose un problème côté serveur final. Ces hypothèses sont parfois justes, souvent partielles, et coûteuses quand on les prend pour argent comptant.

La méthode qui s’en dégage tient en quatre gestes :

  1. Lire l’erreur mot par mot, pas en diagonale. Le host (@localhost), l’encodage, le charset, le port : ces détails sont le diagnostic, pas du décor. La plupart des messages disent plus que ce qu’on en retient au premier coup d’œil.
  2. Croiser avec les inputs réels, au niveau des octets. bin2hex (PHP), Buffer.from(s).toString('hex') (Node), xxd (shell). La donnée affichée ment ; les octets, non. Un caractère invisible ne se voit que là.
  3. Tester l’hypothèse contraire. « Et si le mot de passe était bon, mais le host KO ? » « Et si le numéro était valide, mais l’encodage faux ? » Inverser l’hypothèse implicite du message coûte cinq minutes et fait souvent tomber le vrai coupable.
  4. Écrire le diagnostic posthume dans le repo. Le prochain incident ressemblera au précédent. Une note « @localhost = socket Unix, vérifier le grant @'%' » épargne deux jours à la personne suivante — qui sera peut-être vous, dans six mois.

Lessons learned

#debugging #softwareengineering #incidents #lessonslearned


Partager cet article :

Article précédent
GEO : 6 réglages pour être cité par ChatGPT et Claude
Article suivant
Doublons de n° de facture : MAX+1 et advisory lock