Les requêtes API conditionnelles qui empêchent les agents d'écraser les mises à jour
Les requêtes API conditionnelles protègent les mises à jour pilotées par des agents contre les écrasements obsolètes. Découvrez les ETags, les champs de version, If-Match, les erreurs 412 et les règles sûres de nouvelle tentative.

Un agent peut produire une requête API parfaitement valide et pourtant causer de vrais dégâts. Le problème survient lorsqu'il lit un enregistrement, qu'un autre acteur le modifie, puis que l'agent réécrit son ancienne copie par-dessus l'état plus récent. L'authentification n'empêche rien ici. L'autorisation non plus. La requête vient bien d'un principal autorisé, mais elle repose sur une vision périmée de la réalité.
Les requêtes API conditionnelles corrigent précisément ce problème. Le client dit en substance : « applique cette modification uniquement si la ressource est toujours dans la version que j'ai observée ». Le serveur vérifie cette condition au moment de l'écriture. Si elle est fausse, il refuse l'action avant de modifier quoi que ce soit.
Ce contrat est encore plus important avec les agents de programmation qu'avec une personne qui clique dans un formulaire. Un agent peut lire de nombreuses ressources, s'arrêter pour examiner du code ou exécuter des tests, puis lancer plusieurs écritures alors que le monde a changé. Considérez toute mise à jour importante comme une opération de lecture, modification et écriture, sauf si l'API peut prouver qu'il s'agit d'un ajout ou d'une commande commutative.
Les mises à jour perdues surviennent dans les opérations ordinaires de lecture, modification et écriture
Une mise à jour perdue survient lorsque deux auteurs partent du même ancien état et que l'écriture la plus tardive efface la précédente. Il ne faut ni panne de base de données, ni utilisateur malveillant, ni réseau défaillant. Il suffit d'un serveur qui accepte un remplacement inconditionnel.
Prenons une configuration de déploiement exposée en JSON :
{
"name": "billing-worker",
"replicas": 3,
"image": "registry.example/billing:2.4.0",
"maintenanceMode": false
}
Un agent la lit pour faire passer replicas de 3 à 5 avant un test de charge. Pendant ce temps, un opérateur définit maintenanceMode à true pour examiner un problème de file d'attente. Si l'agent envoie ensuite un PUT complet avec le document qu'il a conservé, il peut remettre maintenanceMode à false. La requête a bien augmenté le nombre de réplicas. Elle a aussi annulé une décision de sécurité que l'agent n'avait jamais vue.
Une mise à jour partielle réduit l'ampleur des dégâts, mais ne supprime pas la course. Si un agent envoie un PATCH pour remplacer /replicas, ce champ peut toujours avoir changé depuis la lecture. Plus important encore, la décision de définir les réplicas à 5 peut dépendre de champs modifiés ailleurs. PATCH décrit la forme du corps de la requête. Il ne dit pas de quelle version de la ressource ce corps dépend.
C'est pourquoi « notre interface ne modifie qu'un seul champ » ne constitue pas une stratégie de concurrence. Une interface peut masquer le problème pendant un certain temps, car les personnes agissent lentement et consultent des pages récentes. Un processus autonome ne bénéficie d'aucune de ces protections accidentelles.
Un ETag identifie la représentation vue par le client
L'en-tête de réponse HTTP ETag est un validateur. Lorsqu'un serveur renvoie une représentation, il peut lui associer un jeton qui identifie cette version :
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "deploy-8f31c2"
{
"name": "billing-worker",
"replicas": 3,
"image": "registry.example/billing:2.4.0",
"maintenanceMode": false
}
La chaîne n'a aucun format interne imposé. Elle peut contenir une révision de base de données, un hachage du contenu ou une valeur opaque générée par le serveur. Les clients doivent la traiter comme une valeur opaque. Ne cherchez pas à extraire un numéro de révision d'une balise et n'en fabriquez pas une à partir d'un corps JSON. Le serveur est seul responsable de sa signification.
La RFC 9110 définit les balises d'entité et distingue les balises fortes des balises faibles. Un ETag fort utilise la syntaxe ordinaire entre guillemets, par exemple "deploy-8f31c2". Il indique que les représentations correspondent octet par octet selon les règles de représentation choisies par le serveur. Une balise faible commence par W/, par exemple W/"deploy-8f31c2", et indique seulement que deux représentations sont suffisamment similaires sur le plan sémantique pour valider un cache.
Cette distinction est souvent brouillée. Les validateurs faibles conviennent à de nombreux contrôles de cache avec GET. Ils ne conviennent pas pour protéger une écriture, car deux représentations « assez proches » peuvent encore différer dans un champ que l'écriture détruirait. La RFC 9110 exige qu'If-Match utilise une comparaison forte. Si votre API ne publie que des ETags faibles, elle ne fournit pas un ETag adapté à la concurrence optimiste.
Une ressource peut avoir des ETags différents selon la représentation. Un JSON mis en forme, un JSON compact ou des formats négociés peuvent chacun recevoir leur propre validateur. C'est un comportement HTTP légitime, mais peu pratique pour les clients d'API. Dans la mesure du possible, conservez une représentation canonique stable pour les points d'accès d'écriture. L'ETag reçu par un client avec GET restera ainsi pertinent pour PUT, PATCH et DELETE.
If-Match transforme le contrôle de version en obligation du serveur
If-Match place l'ETag attendu dans une requête non sûre. Le serveur n'exécute la méthode que si la représentation actuelle correspond fortement à l'une des balises fournies.
Un agent peut lire un enregistrement et conserver l'en-tête reçu :
curl -i \\
-H 'Authorization: Bearer $TOKEN' \\
https://api.example.test/v1/deployments/billing-worker
La réponse contient :
ETag: "deploy-8f31c2"
Il peut ensuite envoyer la plus petite modification prévue, avec le validateur issu de cette lecture :
curl -i -X PATCH \\
-H 'Authorization: Bearer $TOKEN' \\
-H 'Content-Type: application/json-patch+json' \\
-H 'If-Match: "deploy-8f31c2"' \\
--data '[{"op":"replace","path":"/replicas","value":5}]' \\
https://api.example.test/v1/deployments/billing-worker
Si la ressource est toujours dans cette version, le serveur applique le correctif et renvoie un nouvel ETag :
HTTP/1.1 200 OK
ETag: "deploy-a19d77"
Content-Type: application/json
{
"name": "billing-worker",
"replicas": 5,
"image": "registry.example/billing:2.4.0",
"maintenanceMode": false
}
Si la modification de l'opérateur a changé la version actuelle auparavant, le serveur renvoie :
HTTP/1.1 412 Precondition Failed
Content-Type: application/problem+json
{
"type": "https://api.example.test/problems/precondition-failed",
"title": "The deployment changed after it was read",
"status": 412,
"detail": "Fetch the current deployment before retrying this update."
}
La RFC 9110 indique qu'un serveur d'origine ne doit pas exécuter la méthode demandée lorsque la condition If-Match est fausse. C'est cette propriété que vous obtenez. Le contrôle doit avoir lieu dans la même opération atomique que la mutation. Un gestionnaire qui lit la ligne, compare une révision en mémoire, puis écrit plus tard conserve une course entre la comparaison et l'écriture.
Pour une base de données relationnelle, l'implémentation ressemble souvent à une mise à jour conditionnelle :
UPDATE deployments
SET replicas = :replicas,
revision = revision + 1
WHERE id = :id
AND revision = :expected_revision;
Si le nombre de lignes affectées est nul, l'API renvoie 412. S'il vaut un, l'API renvoie le document mis à jour et calcule son nouvel ETag à partir de la nouvelle révision. Placez le test dans la clause WHERE, ou utilisez une primitive transactionnelle équivalente de comparaison et d'affectation. Ne le divisez pas en deux requêtes distinctes en prétendant que la solution est sûre.
Les champs de version exposent le même contrat dans les données applicatives
Un champ de version est un validateur au niveau applicatif. Il fournit aux clients une révision visible qu'ils renvoient dans le corps de la requête, la chaîne de requête ou un en-tête dédié. Cette approche peut être plus simple lorsque les clients utilisent des SDK générés, des files de messages ou des protocoles qui transmettent mal les en-têtes de réponse HTTP.
Un GET peut renvoyer :
{
"id": "billing-worker",
"revision": 42,
"replicas": 3,
"maintenanceMode": false
}
La mise à jour peut exprimer explicitement son attente :
PATCH /v1/deployments/billing-worker HTTP/1.1
Content-Type: application/json
{
"expectedRevision": 42,
"replicas": 5
}
Le serveur compare expectedRevision à la révision stockée de façon atomique. En cas de succès, il incrémente la révision. En cas de différence, il refuse la requête avec une réponse documentée, généralement 412 lorsque le champ joue le rôle de précondition.
Ne confondez pas un champ de version avec un horodatage. Une révision entière monotone rend l'égalité claire. Les horodatages soulèvent des questions difficiles : quelle précision le serveur conserve-t-il ? Deux écritures peuvent-elles tomber dans le même intervalle ? La sérialisation a-t-elle modifié la valeur ? Une réplique attribue-t-elle l'heure différemment ? Il est possible de résoudre certains de ces problèmes, mais un compteur de révision demande moins d'explications.
Les ETags et les champs de version ne s'excluent pas. Une API peut exposer les deux : l'ETag porte la sémantique HTTP standard et la révision aide le code applicatif à afficher ou rapprocher les modifications. Ils doivent provenir du même état validé. Si l'un indique la version 42 et l'autre fait accidentellement référence à la version 41, les clients ne disposent d'aucun moyen fiable de récupérer.
Évitez d'accepter indifféremment un ETag ou un champ de révision s'ils peuvent diverger. Choisissez une précondition faisant autorité pour un point d'accès, ou exigez que les deux concordent. Les contrats d'entrée souples semblent accueillants jusqu'à ce qu'un client envoie une révision obsolète dans le corps et un en-tête fraîchement copié, sans que personne ne sache quelle affirmation le serveur a retenue.
If-None-Match protège la création, pas le remplacement obsolète
If-None-Match inverse le prédicat. Il indique que la méthode ne peut continuer que si la représentation actuelle ne correspond à aucune des balises fournies. Pour les méthodes non sûres, une condition fausse produit 412.
Sa forme de mutation la plus utile est If-None-Match: *, qui signifie « créer ceci uniquement si aucune représentation actuelle n'existe ». Un client peut ainsi tenter en toute sécurité de créer une ressource nommée :
curl -i -X PUT \\
-H 'Authorization: Bearer $TOKEN' \\
-H 'Content-Type: application/json' \\
-H 'If-None-Match: *' \\
--data '{"name":"nightly-export","schedule":"0 2 * * *"}' \\
https://api.example.test/v1/jobs/nightly-export
Si un autre client a déjà créé ce travail, le serveur refuse la requête au lieu de le remplacer silencieusement. C'est utile lorsqu'un agent a déduit un identifiant et ne doit pas s'approprier un objet existant portant le même nom.
N'envoyez pas If-Match: * pour la concurrence optimiste ordinaire. Cette valeur exige seulement qu'une représentation actuelle existe. Elle autorise l'agent à écraser n'importe quelle version actuelle, y compris une version qu'il n'a jamais lue. Elle protège l'existence, pas les mises à jour perdues.
Pour GET et HEAD, If-None-Match prend en charge la mise en cache. Une balise correspondante produit généralement 304 Not Modified, sans corps de réponse. C'est souvent ainsi que les développeurs découvrent les ETags. Ne les considérez pas pour autant comme un simple mécanisme de cache. Le même mécanisme a des conséquences bien plus importantes sur les écritures.
Une réponse à une écriture obsolète exige une politique rigoureuse pour l'agent
Un 412 doit arrêter le plan de mutation en cours. Le raisonnement initial de l'agent n'est plus valable et renvoyer la même requête ne le rendra pas vrai.
La récupération sûre est courte :
- Récupérer la représentation actuelle et son nouveau validateur.
- Comparer les champs ou les hypothèses d'état à l'origine de l'action prévue, et pas seulement le champ nommé dans le correctif.
- Réessayer avec le nouveau validateur uniquement si l'intention reste correcte sans devoir être réinterprétée.
- Demander une approbation ou s'arrêter lorsque l'état actuel modifie le sens, la portée ou le risque de l'action.
C'est au deuxième point que les clients automatisés trichent souvent. Supposons qu'un agent prévoie de retirer un utilisateur d'un groupe d'accès après avoir lu la liste des membres. Une personne change ensuite le rôle de cet utilisateur, qui passe de prestataire à intervenant de réponse aux incidents. L'agent peut encore effectuer une suppression syntaxiquement valide après avoir relu la ressource. Il ne devrait pas le faire automatiquement, car le changement de rôle rend le plan initial douteux.
Conservez un enregistrement de travail simple, mais complet : URI de la ressource, ETag ou révision observé, champs lus, mutation prévue et réponse. Un exécuteur d'outils peut le garder en mémoire pendant une courte tâche. Un flux autonome plus long doit le conserver dans son propre état de tâche audité. Ne demandez jamais au modèle de mémoriser un validateur uniquement à partir d'une description en prose ; les valeurs d'en-tête entre guillemets sont faciles à perdre, modifier ou réutiliser avec la mauvaise ressource.
Sallyport peut empêcher l'agent d'accéder à l'identifiant d'API pendant l'appel HTTP, mais l'agent doit tout de même conserver et envoyer l'ETag comme donnée ordinaire de la requête. L'isolation des identifiants et le contrôle de la concurrence corrigent deux problèmes différents. Utilisez donc les deux lorsque l'action a des conséquences.
412, 409 et 428 décrivent des échecs différents
Renvoyez 412 Precondition Failed lorsque le client a fourni un en-tête de requête conditionnel ou une précondition documentée équivalente, et que cette condition est fausse. La réponse indique précisément que la ressource n'est plus dans l'état affirmé par le client.
Renvoyez 428 Precondition Required lorsque le serveur exige une précondition pour un point d'accès et que le client l'a omise. La RFC 6585 définit ce statut précisément pour empêcher les mises à jour perdues. La réponse peut indiquer que PATCH exige If-Match et inclure l'ETag actuel si sa divulgation ne pose pas de problème.
Renvoyez 409 Conflict lorsque la requête entre en conflit avec l'état applicatif même après validation de sa condition de version. Par exemple, un client peut envoyer une valeur If-Match correspondant à une facture actuelle, alors que le serveur refuse l'annulation parce que le règlement a commencé. Le contrôle de version a réussi ; la commande métier reste incompatible avec l'état de la facture.
Ne réduisez pas ces situations à une erreur générique. L'agent doit réagir différemment :
- Après un 428, récupérer la ressource et réessayer avec la condition requise.
- Après un 412, relire la ressource et réévaluer l'intention initiale.
- Après un 409, examiner le conflit métier et suivre le parcours de résolution prévu par l'API.
Un corps d'erreur utile nomme la ressource, identifie la condition qui a échoué sans révéler de secret et indique si un nouveau GET peut aider. Il ne doit pas laisser croire qu'une nouvelle tentative est sans risque. Le statut HTTP fournit la catégorie lisible par la machine ; le corps donne à l'opérateur assez de contexte pour décider de la suite.
Les dates de dernière modification sont une solution de compatibilité
Last-Modified et If-Unmodified-Since expriment une condition proche : exécuter la méthode uniquement si la ressource n'a pas changé depuis la date fournie. Ils restent utiles lorsqu'une ancienne API publie déjà les dates de modification et que l'ajout de balises prendra du temps.
Ils sont moins fiables pour les écritures importantes. Les dates HTTP ont une précision d'une seconde. Deux modifications dans la même seconde peuvent produire la même date visible, et le client peut ignorer si l'horodatage conservé par le serveur a une précision supérieure à celle de son en-tête. La réplication, les horloges et la sérialisation offrent d'autres occasions de se faire surprendre.
Si un client envoie à la fois If-Match et If-Unmodified-Since, la RFC 9110 donne la priorité à If-Match. C'est logique. Un validateur fort fournit un contrôle exact de la version ; une date n'est qu'une approximation.
Ne créez pas votre propre en-tête X-If-Version sans raison liée au protocole que les en-têtes standard ne peuvent satisfaire. Les en-têtes personnalisés se répandent rapidement dans les SDK et les proxys, puis deviennent un travail de compatibilité permanent. ETag et If-Match ont déjà une sémantique claire, des codes d'état connus et une prise en charge par les outils HTTP courants.
Les formats PATCH doivent eux aussi être testés
Les en-têtes conditionnels protègent la version de la ressource. Ils ne vérifient pas qu'un correctif exprime une transformation sûre. Un JSON Merge Patch qui contient un objet imbriqué entier peut encore effacer les champs voisins, même avec un ETag correct. Un JSON Patch peut cibler la mauvaise position dans un tableau si l'API représente une liste ordonnée dont les éléments ont changé.
Utilisez le format de correctif adapté à l'opération. JSON Patch, défini par la RFC 6902, exprime des opérations comme replace, add, remove et test sur des chemins précis. Son opération test peut vérifier une valeur à l'intérieur du document avant l'exécution des opérations suivantes. JSON Merge Patch, défini par la RFC 7396, décrit un document partiel souhaité et traite null comme une suppression.
Un ETag au niveau du document doit rester la protection principale. Ajoutez un test JSON Patch lorsque l'opération repose sur une hypothèse liée à un champ et que cette hypothèse mérite d'être explicite :
[
{"op":"test","path":"/maintenanceMode","value":false},
{"op":"replace","path":"/replicas","value":5}
]
Si un autre auteur a modifié maintenanceMode avant cette requête, celle-ci doit échouer plutôt que d'augmenter la capacité pendant la maintenance. L'API doit documenter l'erreur renvoyée lorsqu'un test JSON Patch échoue. De nombreuses implémentations utilisent 409, car l'instruction du correctif est en conflit avec le document actuel, tandis que l'incohérence de l'ETag externe reste un 412. Cette distinction aide les clients à savoir s'ils détenaient un document obsolète ou s'ils ont envoyé une requête invalide dépendant de l'état.
Ne vous fiez pas uniquement à un test de correctif comme stratégie générale de concurrence. Il ne protège que les chemins que vous avez pensé à tester. Un ETag fort protège la version de la représentation sur laquelle l'agent a réellement fondé son plan.
Les serveurs doivent appliquer la précondition à la frontière de l'écriture
Un contrat d'API qui recommande simplement If-Match échouera sous la pression des délais. Un client l'omet, un autre SDK oublie de le transmettre et le point d'accès vulnérable devient celui que les agents découvrent dans les exemples. Exigez-le pour les mises à jour où une écriture obsolète aurait un coût réel.
Le gestionnaire doit refuser les conditions manquantes avant d'effectuer des effets de bord. Il doit ensuite transmettre le validateur attendu à l'opération de stockage qui modifie l'état. Pour une ressource répartie entre plusieurs tables ou pilotée par un plan de contrôle externe, regroupez la comparaison et la modification de l'état dans une seule transaction ou utilisez l'opération de comparaison et d'affectation du fournisseur. Si le fournisseur ne le permet pas, votre API ne peut pas honnêtement promettre une protection contre les mises à jour perdues.
Testez volontairement la course. Initialisez une ressource à la révision 7. Faites en sorte que les clients A et B la récupèrent tous deux. Laissez A envoyer un PATCH avec If-Match: "7" et vérifiez qu'il reçoit la révision 8. Laissez ensuite B envoyer un PATCH avec If-Match: "7" et vérifiez qu'il reçoit 412 et que sa modification souhaitée n'apparaît pas. Répétez avec DELETE, PUT complet et toute action groupée qui écrit une ressource après une lecture préalable.
Testez aussi les raccourcis dangereux : un If-Match manquant doit recevoir 428 sur les points d'accès protégés, If-Match: * ne doit pas être présenté comme une protection contre les écritures obsolètes et un ETag faible ne doit pas réussir une comparaison forte. Ces tests détectent les régressions qui surviennent lorsqu'un nouveau point d'accès contourne la méthode habituelle du dépôt.
Rendez le chemin sûr plus facile que l'écrasement
L'API devrait renvoyer des ETags pour chaque GET de ressource modifiable, documenter les conditions requises à côté de chaque opération non sûre et faire en sorte que les méthodes des SDK transportent naturellement les validateurs. Un client ne devrait pas avoir à extraire des en-têtes bruts d'un objet de réponse obscur pour éviter de détruire le travail d'un autre auteur.
Pour les agents, séparez la planification de l'exécution. Lisez la cible, enregistrez son validateur, décrivez la mutation prévue et lancez l'appel conditionnel. Si une observation change, abandonnez l'écriture prévue, sauf si l'agent peut démontrer que ce changement est sans importance. Cette règle paraît prudente parce qu'elle l'est. L'autre solution consiste à autoriser un processus automatisé à agir sur des faits qu'il sait déjà dépassés.
Commencez par les points d'accès où une modification écrasée réveillerait quelqu'un : paramètres de déploiement, contrôle d'accès, fiches client, état des paiements et métadonnées des secrets. Ajoutez If-Match, faites échouer les préconditions manquantes et testez deux auteurs sur le point d'accès. Une fois que le serveur refuse par défaut les écritures obsolètes, la rapidité d'un agent ne transforme plus une concurrence ordinaire en dégâts silencieux.
FAQ
Qu'est-ce qu'un ETag dans une API ?
Un ETag est un validateur HTTP qui identifie une représentation précise d'une ressource. Un client renvoie cette valeur dans If-Match lorsqu'il veut que le serveur ne mette à jour ou ne supprime que la version qu'il a lue. Le serveur doit refuser l'écriture si l'ETag actuel est différent.
Quand une API doit-elle utiliser If-Match ?
Utilisez If-Match pour une mise à jour, un remplacement, une suppression ou toute autre opération qui ne doit s'appliquer qu'à la version examinée par le client. Une balise correspondante autorise la méthode ; une balise différente doit produire 412 Precondition Failed. C'est la protection standard contre une écriture obsolète qui remporterait silencieusement une course entre clients.
Quelle est la différence entre If-Match et If-None-Match ?
If-Match sert à la concurrence optimiste, tandis que If-None-Match empêche généralement une création ou évite de transférer une représentation inchangée. Pour une requête non sûre, If-None-Match: * signifie « effectuer cette opération uniquement si aucune ressource actuelle n'existe ». N'utilisez pas If-None-Match à la place d'une protection pour les opérations de lecture, modification et écriture.
Que doit faire un agent après une réponse 412 ?
Une réponse 412 Precondition Failed signifie que la condition HTTP de la requête est fausse. Le client doit récupérer la représentation actuelle, la comparer à la modification prévue, puis décider s'il doit réessayer, fusionner les changements ou demander l'avis d'une personne. Répéter la même requête obsolète ne ferait que reproduire l'échec.
Puis-je utiliser un champ de version à la place d'un ETag ?
Un champ de version peut convenir si le serveur le compare de façon atomique à la révision stockée pendant l'écriture. Il est souvent plus facile à examiner et à comprendre qu'un ETag opaque. Il ne remplace pas les ETags lorsque des clients génériques et des caches ont besoin des mécanismes conditionnels HTTP standard.
If-Unmodified-Since est-il sûr pour contrôler la concurrence ?
En général, non. If-Unmodified-Since repose sur des horodatages qui peuvent avoir une précision limitée ou un comportement difficile à maîtriser. Cette méthode peut servir de solution de compatibilité, mais un ETag fort ou un champ de version vérifié de façon atomique offre un contrôle plus sûr pour les écritures importantes.
Une mise à jour obsolète doit-elle renvoyer 409 ou 412 ?
409 Conflict signale un conflit métier ou un conflit d'état que le client doit comprendre, par exemple une tentative de fermeture d'un compte associé à une facture impayée. 412 Precondition Failed indique qu'une précondition HTTP explicite est fausse. Retournez l'un ou l'autre selon le problème, au lieu de transformer chaque écriture refusée en 409.
If-Match étoile empêche-t-il les mises à jour perdues ?
If-Match: * signifie que n'importe quelle représentation actuelle convient. Cette condition protège donc uniquement contre la mise à jour d'une ressource inexistante. Elle n'empêche pas d'écraser une version plus récente. Envoyez la valeur exacte de l'ETag lorsqu'un client doit préserver les changements d'un autre auteur.
Chaque point d'accès PATCH doit-il exiger un ETag ?
Exigez une précondition sur les points d'accès où une écriture obsolète peut modifier de l'argent, des permissions, l'état d'un déploiement, des données client ou une configuration. Le serveur peut renvoyer 428 Precondition Required si le client omet la condition requise. Ne l'imposez pas systématiquement aux commandes en ajout seul, qui ne présentent pas de course entre lecture, modification et écriture.
Comment un agent IA doit-il conserver les versions d'une API en toute sécurité ?
L'agent doit conserver l'ETag ou la révision associé à la représentation qu'il a réellement lue, l'envoyer lors de l'écriture protégée suivante et l'abandonner après l'échec d'une condition. Il ne doit jamais inventer une balise, en réutiliser une provenant d'une autre ressource ou résoudre automatiquement un conflit sémantique en écrasant l'état actuel. Un court enregistrement contenant l'URL de la ressource, le validateur observé, les champs visés et la réponse suffit.