L'attente et l'erreur sont des écrans à concevoir
Un traitement IA long peut échouer à chaque étape, et chaque état doit être dessiné : l'attente, l'erreur, le manque de crédits. Je développe Vidiome, un outil qui transforme une vidéo en article. Il enchaîne un téléchargement, une transcription, une génération et une capture d'images, et rien de tout cela n'est instantané.
Cet article décrit les choix d'interface retenus pour ces moments, avec ce que le code en montre. Il part d'un défaut réel, corrigé en juillet 2026 : un échec qui consommait des crédits sans rien livrer. Même logique de terrain que dans l'article sur l'agent IA qui produit les visuels d'un jeu : on raconte ce qui a été mesuré, y compris les ratés.
Un seul toast qui change d'état
La progression d'un traitement long se montre dans un seul toast, pas dans une pile de messages. Chaque étape réutilise le même identifiant, et le texte évolue sur place. Le lecteur suit un message qui avance au lieu de trier plusieurs notifications.
Un toast, un identifiant
Le traitement démarre avec « Démarrage du traitement… ». Puis le même toast devient « Téléchargement et transcription YouTube… », « Génération de l'article… » et « Capture des illustrations… ». Avec la bibliothèque sonner, il suffit de repasser le même id à chaque appel pour remplacer le message sans en créer un nouveau.
Une barre qui dit où, un toast qui dit quoi
L'éditeur suit en parallèle une étape et un pourcentage. L'extraction audio démarre à 5 %, la transcription va de 30 à 70 %, la génération de 80 à 95 %, puis 100 % à la fin. Ces valeurs marquent des étapes franchies, pas un temps restant promis. Le toast nomme l'action, la barre situe l'avancée.
Une erreur qui garde sa première ligne
En cas d'échec, le même toast devient une erreur d'une seule ligne de détail. Son titre est fixe, « Erreur lors du traitement », et sa description reprend uniquement la première ligne du message. Les informations techniques ajoutées sur les lignes suivantes restent dans le journal.
Le lecteur lit une phrase, pas une trace
Une erreur d'outil IA arrive souvent avec un détail du fournisseur, long et illisible pour un non-spécialiste. Couper à la première ligne oblige à écrire cette ligne pour un humain. Le détail complet n'est pas perdu : il part dans les logs, là où quelqu'un saura l'exploiter.
L'échec est enregistré, pas seulement affiché
Quand l'erreur survient, l'article est marqué failed avec sa dernière erreur, et l'éditeur revient à l'état inactif. Rien ne reste bloqué sur un « en cours » qui ne finira jamais. Le toast informe, mais c'est l'état enregistré qui permet de reprendre plus tard.
Dire combien de temps attendre
Quand le serveur limite le débit, le message doit dire quand réessayer. Si la réponse porte un en-tête Retry-After, l'utilisateur lit « Réessayez dans N secondes ». Sinon, un message générique demande de patienter une minute. Le premier cas évite les clics répétés qui aggravent la limitation.
Le dialogue de crédits est réservé au 402
Un manque de crédits ouvre un dialogue, pas un simple toast, parce qu'il appelle une décision. Le statut HTTP 402 (paiement requis) déclenche un dialogue « Crédits insuffisants » qui affiche deux nombres : les crédits requis et les crédits disponibles.
Le texte tient en deux phrases : « Cette action demande X crédits, mais il ne vous en reste que Y. Ouvrez la facturation pour recharger ou passer à l'offre supérieure. » Deux boutons suivent, Annuler et Ouvrir la facturation. Le même échec transforme aussi le toast de chargement en erreur d'une ligne.
La règle qui en découle est simple. Un toast informe sans demander rien, un dialogue pose une décision. Réserver le dialogue au seul cas où l'utilisateur peut agir, ici recharger, garde sa valeur. S'il s'ouvrait pour chaque erreur, plus personne ne le lirait.
Rembourser quand la génération échoue
Les crédits sont débités avant l'appel au modèle, donc un échec doit les rendre. Ce n'était pas le cas au départ. En juillet 2026, une clé de fournisseur mal configurée a révélé le défaut. L'API renvoyait une erreur 500 opaque, « User not found ». Elle consommait les crédits de l'appelant sans rien produire.
Le correctif du 16 juillet 2026
Le commit 2f8a537 corrige trois choses à la fois :
- Remboursement → les crédits débités sont rendus quand la génération lève une erreur, sans jamais masquer l'erreur d'origine ;
- Erreur propre → un échec du fournisseur devient une réponse 502
llm_provider_error, et son détail reste côté serveur ; - Pas de fuite → le repli générique renvoie
internal_errorau lieu du message brut.
Les offres sans déduction de crédits ne sont pas remboursées, puisqu'elles n'ont rien débité. Des tests couvrent ces cas : remboursement après échec, absence de remboursement sans débit, erreur 502 sans fuite, erreur 500 sans fuite.
Ce que le remboursement change pour l'interface
Un remboursement automatique supprime la question « ai-je payé pour rien ? ». Le message d'erreur peut alors rester court, parce qu'il n'a plus à rassurer sur l'argent. L'interface et l'API se répondent : une erreur honnête côté serveur permet un message simple côté écran.
Les règles à reprendre pour votre outil
Ces choix se transposent à tout outil qui lance un traitement long et facturé. Cinq règles suffisent pour les couvrir.
- Un seul toast à identifiant constant pour toute la durée du traitement ;
- Une phrase d'erreur pour l'humain, le détail technique dans le journal ;
- Un dialogue seulement pour ce qui demande une décision, avec les deux nombres utiles ;
- Un délai explicite quand le serveur demande d'attendre ;
- Un remboursement automatique à l'échec, et un test qui le vérifie.
Les mêmes principes valent pour les formulaires, détaillés dans les erreurs courantes d'accessibilité des formulaires : un message court, au bon endroit, qui dit quoi faire ensuite.



