# Dépannage

> Le pied de la console (écran de connexion compris) affiche les deux ; ou flexway version (docker run --rm ghcr.io/flexwiz/flexway:<version> version), build…

Dépannage - Depuis la version 0.1.0 - Flexway v1.6.3 - https://flexway.io/docs/operations-troubleshooting/

> Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la capacité côté produit : [guide produit](https://flexway.io/docs/product-overview/) ; l'appeler : [guide développeur](https://flexway.io/docs/developer-overview/).

| Symptôme | Cause probable | Ce qu'il faut faire |
| --- | --- | --- |
| Quelle version tourne ? | - | Le pied de la console (écran de connexion compris) affiche les deux ; ou `flexway version` (`docker run --rm ghcr.io/flexwiz/flexway:<version> version`), `build` dans le `/config` d'administration. `dev` désigne un build qui n'est pas une release : son commit dit lequel |
| `flexway validate` : `x-flexway-sunset ... is set on an operation that is not deprecated` | Une date de retrait sur une opération (ou héritée de `info`) qui n'est pas dépréciée | Ajouter `deprecated: true` (en gRPC `option deprecated = true;`), ou retirer la date |
| Démarrage refusé : `kms.key "...": kms key absent or unusable` | La clé du chiffrement par champ n'existe pas au KMS (faute de frappe) ou ne peut pas envelopper (clé Vault non dérivée) | Corriger `kms.key` ou créer la clé - [Chiffrement par champ](https://flexway.io/docs/operations-kafka-encryption/) |
| Consommateurs Kafka en `CORRUPT_MESSAGE` après un changement de `kms.key` | Les enregistrements écrits avant nomment l'ancienne clé | La déclarer dans `kms.previous_keys` - [Chiffrement par champ](https://flexway.io/docs/operations-kafka-encryption/) |
| Démarrage refusé : `reads the schema registry's wire format and is isolated by the tenant attribute alone` | Une consommation d'un canal au format filaire du registre filtrée sur l'attribut `tenant`, sans liaison : les messages d'un producteur Kafka ordinaire n'ont aucun attribut | Lier le canal au tenant (`x-flexway-bind: tenant`, adresse `{tenant}.<sujet>`), ou le déclarer public (`x-flexway-public: true`) |
| `POST` d'une porte `x-flexway-publish` : `504 upstream_timeout` | Le courtier n'a pas accusé dans le budget : **issue inconnue**, le message a pu être écrit | Renvoyer avec la même `Idempotency-Key` ; vérifier la santé du courtier, élargir `timeout` |
| `503 overloaded` sur une publication | Le tampon `max_in_flight` du courtier est plein : il accuse moins vite qu'on ne publie | Lire `flexway_async_publish_duration_seconds` ; relever `max_in_flight` ou la capacité du courtier |
| `502 upstream_error` sur une publication | Le courtier a refusé : ACL, sujet absent (la passerelle n'en crée jamais), message rejeté ; AMQP : aucune file ne prend le message | Créer le sujet du tenant, accorder l'écriture au principal de la passerelle, lier une file à l'échange |
| Démarrage refusé : `is consumed and isolates no tenant` | Une consommation sur un canal ni lié au tenant, ni filtré sur l'en-tête `tenant` | `x-flexway-bind: tenant` sur un paramètre du canal, ou l'en-tête `tenant` dans `headers` de chaque message |
| Un abonné WebSocket reçoit un message deux fois | Au moins une fois : pas d'accusé dans `ack_timeout`, ou une coupure avant l'accusé | Accuser chaque `id` (`{"ack": "<id>"}`) ; dédupliquer sur `source` + `id` |
| `flexway validate` échoue sur « opération sans amont » | Une opération ajoutée au contrat sans `x-flexway-upstream`, et pas d'amont `default` | Ajouter l'extension, ou déclarer un amont `default` |
| `flexway import krakend` sort en `3` | Des éléments de la configuration importée n'ont pas d'équivalent ; le reste est écrit | Lire la section « No equivalent » de `coverage.md` : chaque ligne dit pourquoi et, s'il existe, l'équivalent à écrire à la main |
| `flexway import krakend` : `the converted configuration does not load - nothing written` | Le résultat ne démarrerait pas : opération du contrat qu'aucun endpoint ne couvre, agrégation, `sd: dns`, validateur sans `audience`, politique nommée par le contrat et non définie | Le message du chargeur nomme l'opération ou la clé ; poser `x-flexway-upstream` au contrat ou corriger, puis relancer |
| `flexway import krakend` : `is not empty: the import never overwrites` | `--out` contient déjà des fichiers | Un dossier vide ou absent |
| Un endpoint de la configuration importée manque après l'import | Le contrat ne le déclare pas : il est listé dans `coverage.md`, jamais inventé | Le déclarer au contrat, relancer l'import |
| La passerelle refuse de démarrer | Configuration partielle - c'est voulu | Lire le message : il nomme l'opération ou l'amont fautif |
| `404` sur une route qu'on croit exposée | Elle n'est pas dans le contrat. « Servi = déclaré » | L'ajouter au contrat, pas à la configuration |
| Un flux arrive d'un coup à la fin | Un intermédiaire bufferise - pas la passerelle | Vérifier l'edge et tout proxy entre le client et elle |
| Un flux s'arrête au milieu, journal `read budget exhausted` | L'amont s'est tu plus longtemps que `server.timeouts.read` | Faire émettre un battement (`: keepalive`) à l'amont, ou relever `read` |
| `504` `upstream_timeout` | L'amont n'a pas répondu dans le budget de l'opération | Lire `flexway_upstream_duration_seconds` ; relever `x-flexway-timeout` sur **cette** opération, pas le défaut global |
| `Flexway-Cache: miss` à chaque requête | L'amont interdit de garder (`no-store`, `no-cache`, `private`, `Set-Cookie`), répond sans `Content-Length`, au-delà de `server.cache.max_entry_bytes`, ou avec un statut autre que `200`, `203`, `204`, `301`, `404` | Lire les en-têtes de la réponse de l'amont ; `flexway_cache_lookups_total` par opération |
| Aucun `Flexway-Cache` sur une opération cachée | Contournement : appelant identifié sur un cache public, condition `when` fausse, méthode autre que `GET`/`HEAD` | `result="bypass"` dans `flexway_cache_lookups_total` ; un cache par appelant se déclare `scope: subject` à l'opération |
| Démarrage refusé : `response cache declaration(s) refused` | Un cache sur un flux, un WebSocket, gRPC, l'inférence, une écriture, ou public sur une opération authentifiée | Le message nomme l'opération et la porte (`x-flexway-cache` ou la politique) |
| `502` `response_too_large` | Une opération à politique de corps a reçu une réponse au-delà de `server.body.max_bytes` | Relever `server.body.max_bytes` (rechargeable), ou paginer côté amont |
| `502` `upstream_error` sur une opération transformée, journal `an upstream answer could not be transformed` | L'amont a répondu autre chose que du JSON lisible, compressé malgré `Accept-Encoding: identity`, ou sans un champ projeté avec `on_missing: fail` | Le journal nomme la cause et le champ ; corriger l'amont, ou passer à `on_missing: omit` |
| Démarrage refusé : `body policy attachment(s) refused` | Une politique de corps atteint un flux, une réponse non JSON, gRPC ou l'inférence ; ou elle retire un champ requis, garde un champ non déclaré, masque avec un type refusé | Le message nomme l'opération et la politique ; l'attacher plus étroitement ou `!nom` |
| `flexway_policy_nonconformant_total` monte | Un corps servi ne tient plus le schéma de son statut (échantillon) - l'amont a changé, ou le statut n'est pas déclaré | Le journal `departs from its schema` donne le champ et le mot-clé ; la réponse est servie quand même |
| Refus au chargement « `body` is read by the conditions of transform-body, redact and map-status only » | Une autre politique lit `body` dans sa condition ou un gabarit | Lire le corps dans une politique de corps, ou s'en passer |
| `403` (ou autre `4xx`) avec le `code` d'une politique `external-call` | Le service externe a refusé : `deny.when` vaut vrai sur sa réponse | Attendu ; le motif est chez le service, jamais recopié dans le problème |
| `504`, `503` ou `502` sans que l'amont soit appelé, journal `external call` `failed_closed` | Le service externe n'a pas répondu à temps, est injoignable, a son circuit ouvert ou a rendu une réponse illisible pour `deny.when` / `set_headers` - et la politique est `fail-closed` | Le journal donne la politique et la cause ; `flexway_policy_external_calls_total{outcome="failed_closed"}` ; réparer le service, ou `fail-open` si la décision peut manquer |
| Un en-tête attendu d'un `external-call` manque, journal `external call` en `WARN` | `fail-open` a absorbé une panne du service | `flexway_policy_external_calls_total{outcome="failed_open"}` ; le journal donne la cause |
| `504` `upstream_timeout` « budget was spent before its upstream could be called » | Un `external-call` de la phase requête a consommé tout le budget de l'opération | Raccourcir son `timeout`, ou relever `x-flexway-timeout` de l'opération |
| Démarrage refusé : `external-call attachment(s) refused` | `timeout` au-delà du budget d'une opération, `send.body` sur un flux, phase `response` sur un WebSocket, gRPC ou l'inférence | Le message nomme l'opération et la politique |
| `503` `upstream_unavailable` avec `Retry-After` | L'amont refuse les connexions : il est arrêté | Vérifier l'amont ; la passerelle a déjà repris une fois si la méthode le permettait |
| `502` `upstream_error` | L'amont a répondu quelque chose qui n'est pas du HTTP exploitable | Chercher côté amont, pas côté passerelle |
| `401` sur des jetons valides juste après un démarrage | Les clés de l'émetteur ne sont pas encore chargées | Le journal dit `issuer not reachable yet` ; vérifier que la passerelle joint l'URL de découverte |
| `401` `invalid_token` sur un jeton qui marche ailleurs | Audience, émetteur ou algorithme : le motif est au journal (`token refused`), jamais dans la réponse | Comparer `aud` et `iss` du jeton à `auth.issuers` |
| Inférence : `401` `tenant_required` malgré les en-têtes de l'ingress | Le pair n'est pas dans `server.trusted_proxies` : ses en-têtes ne sont pas crus | Déclarer la plage de l'ingress, et elle seule |
| Inférence : `429` `budget_exceeded` | Le budget du tenant (jour ou mois) est épuisé - une requête pré-réserve `max_tokens`, 512 sans lui | Relever `ai.budget` ou une surcharge du tenant ; le compteur est dans `ai.budget.store` |
| Inférence : `429` `rate_limited` pour tous les tenants derrière l'ingress | Une politique de débit trop serrée sur `/v1/*` | Une politique propre sur `routes: { "/v1/**": [...] }` (exemple 12.17) |
| Inférence : `Flexway-Budget: soft` sur toutes les réponses d'un tenant | Il a dépassé `soft.at` de son budget en euros du mois | Attendu ; relever `monthly_eur` du tenant (surcharge) ou attendre le mois suivant |
| Inférence : `Flexway-Degraded: backend-fallback` | Le backend choisi est en panne (connexion, `5xx`, délai, circuit ouvert) ; le suivant de `fallback` a servi | La métrique `flexway.inference.fallback` donne le backend quitté et la cause |
| Refus au chargement « price is required: the budget is counted in euros » | Un budget en euros existe et un backend n'a pas de prix | Déclarer `price` sur chaque backend, `0` pour un interne |
| Inférence : `403` `sensitive_no_internal_backend` | Requête sensible et aucun `ai.sensitive_backend` interne | Déclarer un backend interne ; jamais un externe pour ce flux |
| Inférence : `400` `capability_unavailable` | Le backend que la politique choisit ne sert pas ce que la requête demande - une image sans `vision`, des embeddings sans `embeddings` | Déclarer la capacité sur le backend (`capabilities`) ou une dérogation `match.capability` vers un backend qui la sert |
| Avertissement « is a relay metered locally » (`flexway validate`, démarrage) | Un `kind: relay` sans `metering: upstream` : la Flexway amont compte aussi le coût | Déclarer `metering: upstream` ; le plafond local reste appliqué |
| Inférence : `502` `backend_unavailable` | Backend injoignable, ou sa clé introuvable (`${VAR}` non posée, fichier absent) | Le journal (`inference backend failed`) donne la cause ; la clé est relue à chaque appel |
| Démarrage refusé : `FLEXWAY_CONFIG is not set` | Aucun `flexway.yaml` désigné ; l'ancien `config/policy.yaml` n'est plus lu depuis 0.9.0 | Pointer `FLEXWAY_CONFIG` vers un `flexway.yaml` ; convertir l'ancien fichier ([Servir l'inférence](https://flexway.io/docs/operations-inference/)) |
| `helm install` : `Flexway serves what a contract declares, and none was given` | Ni `openapi` ni `config` dans les valeurs | La commande affichée ([Passer en Kubernetes](https://flexway.io/docs/operations-kubernetes/)) |
| `403` `insufficient_scope` | Le jeton ne porte pas la portée ou le rôle que le **contrat** exige | Lire `scope=` dans `WWW-Authenticate` ; pour un rôle, vérifier `roles_claim` |
| A2A : `200` avec l'erreur JSON-RPC `-32009` (`VERSION_NOT_SUPPORTED`) | Pas d'en-tête `A2A-Version` - c'est la 0.3 pour la spécification | Envoyer `A2A-Version: 1.0` (ou `?A2A-Version=1.0`) ; seule la 1.0 est servie |
| A2A : `403` `PUSH_TARGET_NOT_ALLOWED` | Cible push hors `agents.a2a.push.allowed_hosts`, en `http`, écrite en adresse, avec `user@`, ou aucune liste déclarée | Déclarer le **nom** de l'hôte du webhook ; `flexway_a2a_push_refused_total` donne la raison |
| A2A : `403` `TENANT_MISMATCH` | Le `tenant` de la requête (corps, préfixe de chemin, paramètre) n'est pas celui de l'identité, ou l'identité n'en porte aucun | Envoyer le tenant de l'identité, ou rien : la passerelle transmet le sien |
| A2A : `429` `TOO_MANY_STREAMS` | L'appelant tient déjà `max_streams` flux ouverts vers cet agent sur cette réplique | Fermer un flux, ou relever `max_streams` de l'agent |
| Un flux A2A se termine à heure fixe | `max_task_duration` atteint (`flexway_a2a_calls_total{outcome="duration_exceeded"}`) | Le relever sur l'agent, ou suivre la tâche par `GetTask` |
| Démarrage refusé sur une carte A2A | Liaison gRPC, schéma sans mécanisme, amont sans `token_exchange` ou pas `protocol: a2a`, `signatures` déclarées, `signer` (prévu en `1.9.0`), `verify: none` absent, flux sans `max_streams` ou `max_task_duration` | Le message nomme la carte, l'agent et la règle |
| `401` `WWW-Authenticate: ApiKey` sur une clé qu'on croit bonne | Clé inconnue (retirée, mal copiée), expirée, ou sans le préfixe `fwk_` | Le journal (`API key refused`) donne la cause et, pour une clé expirée, son `id` ; recréer la clé |
| `401` « does not accept API keys » | L'opération n'a pas d'alternative `apiKey` dans le contrat | Ajouter `{ key: [] }` à son `security`, si c'est voulu |
| `403` `not bound to this operation` (clé ou certificat) | L'entrée ne nomme pas cette opération | Ajouter l'`operationId` à `operations` de l'entrée ; rechargement |
| Refus au chargement « no contract declares » sur une clé ou un certificat | L'entrée nomme une opération qui n'existe pas (renommée ?) | Corriger l'`operationId` |
| `401` « requires a client certificate » malgré un certificat | Certificat non déclaré, ou `x-forwarded-client-cert` venu d'un pair hors `server.trusted_proxies` | Vérifier l'URI SAN ou le sujet face à `auth.mtls.clients` ; déclarer la plage de l'edge |
| Poignée de main TLS refusée sur `:8443` | Certificat client d'une autre autorité que `client_ca_file` | Ajouter l'autorité au fichier : il est relu sans redémarrage |
| Refus au chargement « google.api.http ... not supported at this version » | Variable sur plusieurs segments, joker ou verbe dans une règle REST | Une variable par segment (`/v1/shelves/{shelf}/books/{book}`), ou la méthode en gRPC seulement |
| Refus au chargement « is a gRPC method and relays to upstream » | La méthode tombe sur un amont HTTP (le `default`) | Nommer son amont dans `flexway.v1.operation.upstream`, déclaré `protocol: grpc` |
| Client gRPC : `UNIMPLEMENTED` | La méthode n'est pas dans le jeu servi - jeu périmé | Reconstruire le `.binpb` : il est relu sans redémarrage |
| REST : `upstream_status` | L'amont gRPC a répondu une erreur : c'est sa réponse, pas un échec de la passerelle | Lire `grpc.status` et `grpc.details` du problème |
| `400` « Query parameter ... is not declared » | Fermé par défaut : le contrat ne déclare pas ce paramètre | Le déclarer dans le contrat, pas contourner la passerelle |
| `429` `rate_limited` | Le seau de l'appelant est vide | Lire `Retry-After` ; relever `rps`/`burst` de la classe, ou changer la classe de l'opération |
| Tous les anonymes semblent partager un seau | La passerelle voit l'adresse du bord - ou, derrière un load balancer cloud sans proxy protocol, celle du load balancer - pas celle du client | Déclarer le bord dans `server.trusted_proxies` ; lire `x-forwarded-for` dans le journal d'accès du bord, et activer le proxy protocol au load balancer et au bord ([Poser des quotas](https://flexway.io/docs/operations-quotas/) du guide) |
| `422` `invalid_request` | La requête ne respecte pas le schéma du contrat | L'extension `errors` nomme le champ et la règle |
| `503` avec `Flexway-Degraded: circuit-open` | L'amont échoue ; le coupe-circuit répond à sa place | Réparer l'amont ; un appel d'essai passe après `cooldown` |
| `503` avec `Flexway-Degraded: upstream-saturated` | `max_in_flight` atteint sur cet amont | Relever le plafond, ou trouver pourquoi l'amont ralentit |
| `503` `overloaded` avec `Flexway-Degraded: shedding`, `flexway_shed_total{reason="in_flight"}` qui monte | La réplique est à son plafond de requêtes en vol (`server.in_flight`, dérivé de sa limite mémoire s'il n'est pas écrit) : elle refuse l'excédent au lieu d'être tuée (OOM) | Ajouter des répliques, ou plus de CPU et de mémoire à chacune ; la limite en vigueur est dans `/config` (`server.in_flight`) et au journal de démarrage |
| Un en-tête n'arrive pas à l'amont | Il n'est pas déclaré dans le contrat - il est retiré, pas refusé | Le déclarer comme paramètre `in: header` |
| `403` « This origin is not allowed » | L'origine du navigateur n'est dans aucune politique `cors` | L'ajouter à `allow_origins` |
| Compteurs de quota incohérents entre répliques | Pas de magasin partagé | Déclarer un Redis, Dragonfly ou Valkey |
| Une modification n'est pas prise | La nouvelle version est refusée : l'ancienne sert toujours | Lire `last_reload.cause` dans `/config` ; en grappe, vérifier que le `ConfigMap` n'est pas monté en `subPath` |
| Rechargement refusé « restart required » | `server.shutdown`, `observability` ou `server.reload` a changé | Redémarrer : c'est le seul moyen de les appliquer |
| `404` sur un hôte qui déclare `contracts` | Cet hôte sert **exactement** ses contrats, rien du contrat par défaut | Ajouter le contrat voulu à `hosts.<hôte>.contracts` |
| Agent : `401` malgré un jeton valide | Le jeton n'est pas émis **pour la surface** : son audience doit être l'URL de `agents.mcp.resources` | Demander le jeton avec l'indicateur de ressource (`resource=https://…/mcp`) |
| Un outil attendu manque dans `tools/list` | Le jeton de l'agent n'a pas la portée ou le rôle que l'opération exige - la liste est filtrée | Donner la portée à l'agent, pas élargir l'opération |
| Un outil répond `401` depuis l'amont | L'amont vérifie lui-même un JWT, et le jeton de l'agent ne lui est jamais relayé | Déclarer `upstreams.<nom>.token_exchange` et le `client` de l'émetteur ([Le jeton que reçoit l'amont](https://flexway.io/docs/operations-agents-mcp/#le-jeton-que-re%C3%A7oit-lamont), [Jeton échangé vers l'amont](https://flexway.io/docs/operations-examples-agents/#jeton-%C3%A9chang%C3%A9-vers-lamont)) |
| Un outil d'un serveur fédéré n'apparaît pas | Serveur injoignable ou pas encore découvert ; outil hors de la liste déclarée ; portée manquante ; nom déjà pris | `/config`, `mcp_federation` : état, outils servis, `not_served` et pourquoi ; le journal dit `MCP server not reachable` |
| Refus au chargement « is offered twice … rename one » | Un outil fédéré porte le nom d'un outil du contrat ou d'un autre serveur | `agents.mcp.servers.<nom>.rename` |
| Un outil fédéré répond `forbidden` | L'appelant n'a pas les `scopes` (ou `tool_scopes`) déclarés, ou n'est pas authentifié | Donner la portée à l'agent |
| `403` « The chain of agents … is longer than allowed » ou « older than allowed » | Le jeton porte plus d'agents (`act`) que `max_depth`, ou l'humain s'est authentifié il y a plus de `max_age` - l'opération peut resserrer | Le journal (`delegation chain refused`) donne la raison et les agents ; réauthentifier l'humain, ou raccourcir la chaîne |
| `401` « The delegation was revoked » | L'émetteur répond que le jeton n'est plus actif (introspection) | Attendu après une révocation ; obtenir un nouveau consentement |
| `503` « The delegation cannot be verified now » | L'introspection de l'émetteur est injoignable, ou l'émetteur n'en publie pas | Vérifier `introspection_endpoint` dans sa découverte et le `client` ; un jeton direct, lui, passe |
| `403` sur un amont qui échange, journal `widened` | Le fournisseur a accordé une portée que l'appelant n'avait pas | Régler la politique d'échange du fournisseur : un saut ne doit jamais élargir |
| Découverte en échec `401` | Le serveur exige un jeton même pour lister | `discovery.issuer`, avec un `client` sur cet émetteur |
| `403` « The identity provider refused to issue a token for this upstream » | Le fournisseur refuse l'échange : client non autorisé à échanger, audience inconnue, secret faux | Le journal (`token exchange refused`) donne le code du fournisseur ; régler le client côté fournisseur |
| `503` « could not issue a token », avec `Retry-After` | Fournisseur injoignable, ou secret client introuvable (`${VAR}` non posée, fichier absent) | Le journal (`token exchange unavailable`) dit lequel ; le secret est relu à chaque échange, sans rechargement |
| Refus « operationId … is declared by both » | Deux contrats nomment deux opérations pareil | En renommer une : l'identifiant indexe politiques, métriques et `/config` |
| Refus au démarrage « is protected - the gateway alone writes it » | Une politique écrit un en-tête que seule la passerelle pose (identité, trace, tenant, `X-Forwarded-*`) | Choisir un autre nom d'en-tête |
| L'amont reçoit `X-User-Roles: [seller admin]` | Passerelle antérieure à `1.6.0` : une liste rendue à la manière de Go | Mettre à jour : une liste arrive `seller,admin` ; `{{ auth.claims.roles.join(' ') }}` pour un autre séparateur ([Enrichir un flux](https://flexway.io/docs/operations-policies/)) |
| Refus au démarrage `undeclared reference to 'x'` ou `found no matching overload` dans une valeur | Une fonction que le langage n'a pas, ou `join` sur un séparateur qui n'est pas du texte | Seules les fonctions de CEL et `join` existent ; corriger l'expression |
| Refus « segment … has two parameters side by side » ou « a '{' that is never closed » | Un segment de chemin que le routeur ne sait pas lire | Mettre un texte entre les deux paramètres, fermer l'accolade |
| Refus « segments … rank the same and can match the same text » | Deux segments mixtes de même rang lisent le même texte (`a{x}` et `{x}a`) | Rendre l'un plus littéral, ou renommer un chemin |
| `404` sur `/reports/2026.pdf` alors que `/reports/{year}.pdf` est au contrat | Passerelle antérieure à `1.6.0` : le segment mixte était pris pour un littéral | Mettre à jour ([Servir sa première API](https://flexway.io/docs/operations-first-api/)) |
| Première requête lente après un démarrage, sur un grand contrat en `validation: params` | Passerelle antérieure à `1.6.0` : le validateur des paramètres était construit à sa première requête, après `/readyz` | Mettre à jour : les deux validateurs sont compilés avant `/readyz` ; la première requête vers un amont ouvre encore sa connexion |
| Refus au démarrage « on_missing is required » | Une valeur est une expression `{{ … }}` sans dire quoi faire si elle manque | Ajouter `on_missing: skip`, `empty` ou `fail` |
| Refus au démarrage « two … policies at the same level » | Deux `cors` (ou `rate-limit`, `rewrite-path`) au même niveau | En garder une, ou déplacer l'autre vers un niveau plus précis |
| `500` `internal_error`, journal `a policy value is missing` | `on_missing: fail` et la valeur manque pour cet appelant | Ajouter une condition `when`, ou passer à `skip` |
| Une politique n'agit pas | Le journal de démarrage dit `NOT applied` : son type vient d'une version ultérieure | Lire `effective_chains` dans `/config` |

---

> **Sur les langues.** Cette documentation existe en français et en anglais, page pour page ; les
> deux sont tenus à jour ensemble. Les spécifications, les ADR et la feuille de route sont en
> français - c'est le dossier d'ingénierie. Le produit lui-même, ses clés de configuration, ses
> codes d'erreur et ses journaux sont en anglais.
