Javier Eguiluz a ouvert le 1er juillet une PR qui redessine entièrement la page d'exception de Symfony. Deux changements se détachent : la chaîne d'exceptions devient une timeline cliquable, et un bouton « Copy as text » formate stack trace et logs pour un collage direct dans un assistant IA. Ce billet regarde ce que ce bouton dit de nos habitudes de debug, et ce que la discussion publique de la PR a déjà fait bouger.
Une timeline à la place de l'empilement
Aujourd'hui, la chaîne d'exceptions s'affiche en breadcrumbs, avec les détails de toutes les exceptions empilés sur la même page. Quand une exception Doctrine est emballée dans une exception Twig, elle-même emballée dans une HttpException, la page devient un mur de stack traces. L'œil cherche la cause racine à la barre de défilement.
La proposition : une timeline, un item par exception, et un seul détail affiché à la fois. On clique sur le maillon qui nous intéresse, on voit sa trace, ses arguments, ses logs. La hiérarchie de la chaîne devient un élément de navigation au lieu d'être un accident de mise en page.
Le bouton qui reconnaît un deuxième lecteur
La page d'exception a toujours été écrite pour un lecteur humain : couleurs, code plié, extraits de fichiers. Le bouton « Copy as text » acte un changement d'époque : le premier réflexe de beaucoup de devs devant un crash est désormais de le coller dans un assistant. Et ce qu'on colle aujourd'hui est mauvais : une capture d'écran, ou une sélection à la souris qui embarque la soupe HTML de la page.
Le bouton formate l'ensemble (trace complète, chaîne d'exceptions, logs de la requête) en texte propre, pensé pour être lu par une machine. C'est un petit bouton et une grande admission : le framework optimise maintenant le chemin qui va de l'erreur à l'agent, pas seulement de l'erreur à l'œil.
Un autre regard
WebMCP : exposer un site aux agents
La PR joint un exemple complet du texte copié. Deux sections balisées en markdown, « Stack trace » puis « Logs ». La trace liste chaque frame en texte brut, avec fichier et ligne, du FormRegistry de vendor jusqu'au public/index.php. Les logs suivent, horodatés, avec canal et niveau : la route qui matche, la sécurité qui recharge l'utilisateur, la requête SQL, puis le CRITICAL final. Le collage d'aujourd'hui demande trois allers-retours (la trace, les logs, le contexte de la requête) et perd la structure en route. Ici, tout arrive en un seul bloc hiérarchisé, lisible par une machine sans nettoyage préalable.
Pour aller plus loin
Une IA a trouvé les failles que ma QA ne voyait plus
On peut y voir un gadget. On peut aussi y voir la première brique d'un contrat plus sérieux entre les frameworks et les outils d'assistance. Si la page d'erreur de dev devient une interface machine, la question de son format (structure, verbosité, masquage des secrets) devient une question d'API, et plus seulement de CSS.
Le design en public, et ce qu'il a déjà cédé
La PR se lit comme un mini produit en itération publique, et le fil des commentaires vaut le détour. Jour de l'ouverture : JoppeDC regrette le rouge historique du header, captures comparatives à l'appui, et propose un fond rouge léger. Javier défend d'abord ses maquettes : les captures font 8 000 pixels de large, tout y paraît plus pâle que dans une vraie app, et il invite chacun à tester le vrai patch dans sa propre app.
Le lendemain, il cède : les plaintes ont continué sur le Slack de Symfony, le rouge revient. Crovitche-1623 juge alors cette version « trop agressive ». Le 10 juillet, Javier repropose un header « très rouge » pour les uns, « mais pas trop » pour les autres. Quatre allers-retours publics sur une couleur de bandeau.
Les autres fils suivent le même mouvement. Kocal trouve les arguments de méthode multicolores trop chargés dans la trace, et demande une palette accordée aux blocs de code. BackEndTea pointe une régression discrète : avant, on pouvait tout déplier d'un coup et chercher au Cmd+F dans la page entière.
yceruto suggère de surligner le texte trouvé dans la recherche de logs, gentiment renvoyé à une PR future. FluffyDiscord ouvre au lance-flammes (« I hate this, actually »), se voit demander un retour constructif par Kocal, puis précise : l'ordre des exceptions semble inversé, et il voudrait pouvoir couper les animations.
Le débat sur l'ordre est le plus instructif. chalasr demande l'exception principale d'abord et les précédentes ensuite, comme PHP et Symfony les affichent partout ailleurs. Javier répond, sincèrement perplexe : le code utilise le même array_reverse() qu'avant. MatTheCat lève le lièvre : ce array_reverse() ne s'appliquait qu'aux breadcrumbs, jamais au détail des exceptions. Personne n'avait remarqué l'incohérence tant que la hiérarchie restait décorative. Dès qu'elle devient l'outil de navigation, l'ordre devient une décision à assumer.
C'est une leçon transposable à n'importe quel projet : exposer une refonte tôt, encaisser les retours esthétiques sans les confondre avec les retours fonctionnels, et garder une trace publique des arbitrages. La page d'exception est probablement l'écran le plus vu par les devs Symfony après leur IDE : chaque détail y trouve des contradicteurs.
Ce que ça change pour votre app
Rien en production, et c'est le point à bien comprendre. Symfony traite toute erreur comme une exception, mais l'affiche via deux mécanismes distincts. En dev, quand le mode debug est actif, le composant ErrorHandler dessine la grande page d'exception : trace, extraits de code, logs. C'est cette page, et elle seule, que la PR redessine.
En prod, pour des raisons de sécurité, l'utilisateur voit une page générique. Elle se personnalise en surchargeant des templates dans templates/bundles/TwigBundle/Exception/ : error404.html.twig, error403.html.twig, et error.html.twig pour tout le reste, 500 comprises. Pour un contrôle complet, l'option framework.error_controller branche un controller maison. Et pour prévisualiser ces pages sans provoquer de vraie erreur, la doc fournit des routes de test à monter en dev sous /_error/{code}.
Vos surcharges de prod ne bougeront donc pas d'un pixel. La refonte vise la branche 8.2 : même côté debug, rien n'arrivera avant cette version.
Et ensuite ?
La PR n'est pas mergée (vérifié le 11 juillet). Tout ce qui précède peut bouger, y compris le bouton. Si vous surchargez les templates d'erreur ou le style de la page de debug, n'adaptez rien avant la version finale. Et la page d'erreur de prod (error.html.twig) ne change pas, comme détaillé plus haut : on parle exclusivement de l'écran de debug.
La question que la PR ne tranche pas : si l'écran de dev apprend à parler aux assistants, qu'est-ce qu'on fait de la prod ? Une erreur 500 en production finit dans les logs, dans un tracker d'erreurs, parfois dans une réponse RFC 9457. Côté API, ce RFC (les « Problem Details ») a déjà fait le chemin que le bouton amorce côté debug. Il remplace la page d'erreur HTML par un objet JSON structuré : un type, un titre, un statut, un détail. Un client machine le lit sans rien scraper ni deviner. « Copy as text » applique la même logique à l'écran de dev : donner à l'erreur un format de sortie pensé pour son lecteur réel. Reste le trou entre les deux : aucun des formats de prod n'a été pensé pour être collé tel quel dans un agent. Il y a un chantier là.
Une coquille, une erreur dans ce billet ? Signale-la-moi.