flexway
FRENParler à un expert

Dépannage

DépannageExploitationDepuis la version 0.1.0

Markdown

Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la capacité côté produit : guide produit ; l'appeler : guide développeur.

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
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
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)
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)
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 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, Jeton échangé vers l'amont)
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)
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)
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.

Étapes suivantes