diff --git a/app/but/bulletin_but_court.py b/app/but/bulletin_but_court.py
index a4fc6ec13ef864fc23aa7b2b6d05e91aff2e6254..41f989a11ede42ec2fe437d8e9399f14228a1959 100644
--- a/app/but/bulletin_but_court.py
+++ b/app/but/bulletin_but_court.py
@@ -124,7 +124,9 @@ def _build_bulletin_but_infos(
         formsemestre, bulletins_sem.res
     )
     if warn_html:
-        raise ScoValueError("<b>Formation mal configurée pour le BUT</b>" + warn_html)
+        raise ScoValueError(
+            "<b>Formation mal configurée pour le BUT</b>" + warn_html, safe=True
+        )
     ue_validation_by_niveau = validations_view.get_ue_validation_by_niveau(
         refcomp, etud
     )
diff --git a/app/comp/res_classic.py b/app/comp/res_classic.py
index 113faa0b2afad7cb1348202cb2ac9edbcc0f4959..59f3790e4ca29332d2bdcf021906a8c3169f836e 100644
--- a/app/comp/res_classic.py
+++ b/app/comp/res_classic.py
@@ -242,7 +242,8 @@ class ResultatsSemestreClassic(NotesTableCompat):
             )
             }">saisir le coefficient de cette UE avant de continuer</a></p>
             </div>
-            """
+            """,
+            safe=True,
         )
 
 
diff --git a/app/comp/res_common.py b/app/comp/res_common.py
index 9915c616259a91b3510213415f08784f8b30f3fe..267f5e7fad3e2736658eae92bf0768a71ce5d446 100644
--- a/app/comp/res_common.py
+++ b/app/comp/res_common.py
@@ -518,7 +518,8 @@ class ResultatsSemestre(ResultatsCache):
                     Corrigez ou faite corriger le programme
                     <a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
                     formation_id=ue_capitalized.formation_id)}">via cette page</a>.
-                    """
+                    """,
+                        safe=True,
                     )
             else:
                 # Coefs de l'UE capitalisée en formation classique:
diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py
index 80cee3a4d7298ab01a898729676469e4ad65db75..dddd4430ee5e58822096366ff3eb83bf1121c786 100644
--- a/app/scodoc/sco_bulletins_pdf.py
+++ b/app/scodoc/sco_bulletins_pdf.py
@@ -122,7 +122,8 @@ def replacement_function(match) -> str:
         return r'<img %s src="%s"%s/>' % (match.group(2), logo.filepath, match.group(4))
     raise ScoValueError(
         'balise "%s": logo "%s" introuvable'
-        % (pydoc.html.escape(balise), pydoc.html.escape(name))
+        % (pydoc.html.escape(balise), pydoc.html.escape(name)),
+        safe=True,
     )
 
 
diff --git a/app/scodoc/sco_dump_db.py b/app/scodoc/sco_dump_db.py
index a74fab8d3f9dc3fa9d00b28daa33ec6217c73f23..1bef54391555f895ea3499d4ed16b17831224b21 100644
--- a/app/scodoc/sco_dump_db.py
+++ b/app/scodoc/sco_dump_db.py
@@ -216,11 +216,11 @@ def _drop_ano_db(ano_db_name):
         log("_drop_ano_db: no temp db, nothing to drop")
         return
     cmd = ["dropdb", ano_db_name]
-    log("sco_dump_and_send_db: {}".format(cmd))
+    log(f"sco_dump_and_send_db: {cmd}")
     try:
         _ = subprocess.check_output(cmd)
-    except subprocess.CalledProcessError as e:
-        log("sco_dump_and_send_db: exception dropdb {}".format(e))
+    except subprocess.CalledProcessError as exc:
+        log(f"sco_dump_and_send_db: exception dropdb {exc}")
         raise ScoValueError(
-            "erreur lors de la suppression de la base {}".format(ano_db_name)
-        )
+            f"erreur lors de la suppression de la base {ano_db_name}"
+        ) from exc
diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py
index 1a951e996f04d5153bc3b21f79d6620faddc796c..6def9f0faae9ab37e141f1c88579372eed42620d 100644
--- a/app/scodoc/sco_edit_formation.py
+++ b/app/scodoc/sco_edit_formation.py
@@ -326,6 +326,7 @@ def do_formation_create(args: dict) -> Formation:
                 scodoc_dept=g.scodoc_dept,
                 formation_id=formation.id,
             ),
+            safe=True,
         ) from exc
 
     ScolarNews.add(
diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py
index aa2d04d0e2fde54903f816cbdfc859302ac73b31..2e61608c32559f5b693a7cf9127fea2568186749 100644
--- a/app/scodoc/sco_exceptions.py
+++ b/app/scodoc/sco_exceptions.py
@@ -45,13 +45,17 @@ class ScoInvalidCSRF(ScoException):
 
 
 class ScoValueError(ScoException):
-    "Exception avec page d'erreur utilisateur, et qui stoque dest_url"
+    """Exception avec page d'erreur utilisateur
+    - dest_url : url où aller après la page d'erreur
+    - safe (default False): si vrai, affiche le message non html quoté.
+    """
 
     # mal nommée: super classe de toutes les exceptions avec page
     # d'erreur gentille.
-    def __init__(self, msg, dest_url=None):
+    def __init__(self, msg, dest_url=None, safe=False):
         super().__init__(msg)
         self.dest_url = dest_url
+        self.safe = safe  # utilisé par template sco_value_error.j2
 
 
 class ScoPermissionDenied(ScoValueError):
diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py
index 3f5008e3e5142a292abbbdcd3e4637b082e36600..19c64367d5faee4eed0921c376b20e7d6f0e22e7 100644
--- a/app/scodoc/sco_synchro_etuds.py
+++ b/app/scodoc/sco_synchro_etuds.py
@@ -115,7 +115,8 @@ def formsemestre_synchro_etuds(
             url_for('notes.formsemestre_editwithmodules',
                 scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
         }">Modifier ce semestre</a>)
-        """
+        """,
+            safe=True,
         )
     footer = html_sco_header.sco_footer()
     base_url = url_for(
diff --git a/app/templates/sco_value_error.j2 b/app/templates/sco_value_error.j2
index 6fa329c7b2d6c7c30984a91dd9aac00d998e7154..2a488c933eb09522e94c6dc2d3273c261ba82279 100644
--- a/app/templates/sco_value_error.j2
+++ b/app/templates/sco_value_error.j2
@@ -5,15 +5,19 @@
 
 <h2>Erreur !</h2>
 
-{{ exc }}
+{% if exc.safe %}
+    {{ exc | safe }}
+{% else %}
+    {{ exc }}
+{% endif %}
 
 <div style="margin-top: 16px;">
     {% if g.scodoc_dept %}
-    <a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
+    <a class="stdlink" href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
     {% elif exc.dest_url %}
-        <a href="{{ exc.dest_url or url_for('scodoc.index') }}">continuer</a>
+        <a class="stdlink" href="{{ exc.dest_url or url_for('scodoc.index') }}">continuer</a>
     {% else %}
-        <a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
+        <a class="stdlink" href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
     {% endif %}
 </div>
 
diff --git a/app/views/notes.py b/app/views/notes.py
index 1add286a90ed6ddbe0591f3c5e6c3b4f1d09960d..51a74b3e7e88455b5a0c524ace6647c9f581d4d1 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -1436,14 +1436,14 @@ def formsemestre_desinscription(etudid, formsemestre_id, dialog_confirmed=False)
     if nt.etud_has_decision(etudid):
         raise ScoValueError(
             f"""Désinscription impossible: l'étudiant a une décision de jury
-            (la supprimer avant si nécessaire:
-            <a href="{
+            (la supprimer avant si nécessaire avec
+            <a class="stdlink" href="{
                 url_for("notes.formsemestre_validation_suppress_etud",
                 scodoc_dept=g.scodoc_dept, etudid=etudid,
                 formsemestre_id=formsemestre_id)
-            }">supprimer décision jury</a>
-            )
-            """
+            }">supprimer décision jury</a>)
+            """,
+            safe=True,
         )
     if not dialog_confirmed:
         etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]