Erreurs
Forme
La plupart des points de terminaison retournent un corps JSON en cas d'échec :
{ "error": "message lisible", "code": "code-machine" }error est toujours présent. code est présent sur chaque échec d'authentification et
d'application de forfait, et sur chaque échec de l'API de compte. La plupart des 400 du chemin
de données ne portent que error, parce que le message nomme le paramètre exact en cause et que
c'est la partie utile.
Les tuiles, le terrain, les courbes de niveau, les glyphes et les sprites font exception.
Leurs corps d'erreur sont du texte brut, pas du JSON : invalid tile coordinates,
invalid fontstack, not found, etc. MapLibre GL ne lit jamais ces corps; il vérifie seulement le
code HTTP et continue. Tous les autres points de terminaison de la passerelle (géocodage,
itinéraires, styles et l'API de compte) retournent du JSON, tout comme la seule erreur JSON que les
chemins de terrain et de courbes de niveau émettent, le 404 d'une archive non configurée.
Union code
type ErrorCode =
| "unauthorized" // 401 : clé absente, mal formée ou inconnue
| "rate_limited" // 429 : au-delà de la limite de débit par clé
| "quota_exceeded" // 429 : forfait hobby au bout de son volume mensuel inclus
| "spend_cap" // 429 : forfait payant au plafond de dépenses
| "billing_required" // 429 : paiement échoué sur un forfait payant
| "origin_not_allowed" // 403 : la liste d'origines de cette clé ne couvre pas la requête
| "addon_required" // 403 : forfait payant, la clé n'a pas la capacité nommée
| "dev_only" // 403 : forfait dev, capacité premium, origine de production
| "usage_limit_reached" // 403 : forfait dev au-delà de son plafond d'appels premium
| "invalid_theme" // 400 : un code `?theme=` qui ne se décode ou ne se valide pas
| "invalid_location" // 400 : /route, /matrix, /match, /optimized-route : une coordonnée manque ou est mal formée
| "invalid_mode" // 400 : /route, /isochrone : un mode de déplacement inconnu
| "invalid_route_option" // 400 : une dimension de camion, avoid, use_highways, use_hills ou un plafond
// de contour hors limites ou inconnu, ou envoyé sur un mode dont
// le calcul de coût ne connaît pas cette option; aussi
// avoid=highways envoyé avec use_highways
| "invalid_body" // 400 : /matrix, /match, /optimized-route : le corps manque, n'est pas du JSON, ou dépasse la limite de taille
| "too_many_locations" // 400 : /matrix, /match ou /optimized-route a dépassé son plafond d'origines, de destinations, de points ou d'arrêts
| "no_route" // 422 : /route, /matrix, /match, /optimized-route : le graphe n'offre aucune réponse
| "routing_failed" // 502 : le conteneur de routage a échoué pour une autre raison
| "service_unavailable" // 503 : un service porté par conteneur n'a pas répondu à temps
| "invalid_token" // API de compte seulement : lien magique ou jeton de session invalide ou expiré
| "not_found" // API de compte seulement
| "conflict" // API de compte seulement : l'état du traitement ne le permet pas
| "payload_too_large" // 413 : POST /geocode/batch avec plus de 100 requêtes; aussi un téléversement de compte au-delà du plafond
| "plan_required" // API de compte seulement : exige un forfait payant
| "bad_request"; // API de compte seulementLes dix-huit premiers sont ceux que le trafic par clé API de votre application peut rencontrer. Les
six derniers appartiennent aux points de terminaison du tableau de bord (/account/*), qui
s'authentifient par session navigateur plutôt que par clé API. invalid_theme et les sept codes
d'itinéraires ci-dessus sont les réponses 400, 422 et 502 du chemin de données qui portent un
code : un thème ignoré en silence est exactement la panne que les utilisateurs d'un générateur de
thèmes ne peuvent pas déboguer, et un échec d'itinéraire codé permet à un client de distinguer
too_many_locations de invalid_location sans analyser le message. invalid_theme porte aussi un
tableau detail qui nomme ce qui clochait dans le code. Voir Itinéraires
pour savoir quel point de terminaison émet quel code d'itinéraire, et ROUTING_ERROR_CODES dans
apps/gateway/src/routing/errors.ts pour la liste source.
Sur le chemin de données, payload_too_large est le 413 de POST /geocode/batch lorsque
queries a plus de 100 entrées. Découpez la liste; ne réessayez pas le même corps. Les codes
de compte conflict et plan_required appartiennent à l'ancien téléversement hébergé, pas à
POST /geocode/batch. Il n'y a pas d'API de traitements aujourd'hui; voir
Géocodage par lots.
En-têtes lisibles par un navigateur
Le JavaScript multi-origine ne voit que les en-têtes de réponse que le serveur expose
explicitement, et un en-tête que votre code ne peut pas lire est un en-tête inexistant du point de
vue de votre logique de reprise. L'API expose Retry-After, ETag, Content-Length,
Content-Range et Accept-Ranges.
Retry-After est celui qui compte ici : il accompagne chaque 429 sauf billing_required, ainsi
que le 503 réessayable, de sorte qu'un client navigateur peut le respecter au lieu de deviner un
délai.
Codes HTTP
401: clé API absente, mal formée ou inconnue. Codeunauthorized. Chaque401du chemin de données porte aussi un défiWWW-Authenticate(RFC 6750) qui nomme le schéma et pointe vers les métadonnées qui l'expliquent :Bearer realm="api.unmap.dev", resource_metadata="https://api.unmap.dev/.well-known/oauth-protected-resource", avecerror="invalid_token"en plus lorsqu'une clé a été présentée mais n'a pas été reconnue.429: l'une de quatre situations, distinguées parcode:rate_limited: au-delà de la limite de débit par clé (1 000 requêtes par 60 secondes par emplacement Cloudflare).Retry-After: 60.quota_exceeded: une clé hobby au-delà de ses 10 000 appels inclus pour le mois.Retry-After: 3600; le blocage se lève à la prochaine remise à zéro mensuelle.spend_cap: une clé payante à son plafond de dépenses configuré.Retry-After: 3600; augmentez le plafond dans le tableau de bord.billing_required: le dernier paiement d'un forfait payant a échoué. Pas deRetry-After, parce qu'aucune attente ne règle la situation; mettez à jour le mode de paiement dans le tableau de bord.
403: l'une de quatre situations, distinguées parcode:origin_not_allowed: la clé porte une liste d'origines autorisées non vide et cette requête n'y est pas couverte. Un en-têteOriginabsent, un en-tête illisible et une origine bien formée mais simplement absente de la liste reçoivent tous la même réponse, et le corps ne dit jamais ce que contient la liste. Une clé dont la liste est vide n'a aucune restriction et ne voit jamais cette erreur. Voir Authentification.addon_required: une clé sur forfait payant qui ne détient pas la capacité dont cette couche ou cette superposition a besoin. Le corps portecapability(legalLand,energy,agriculture,remoteoumining),addonqui nomme le SKU le moins cher qui l'accorderait (addon-legal-land,addon-energy,addon-agriculture,addon-remote,addon-mining), et unmessageécrit pour une personne là oùerrorest écrit pour un journal. Voir Couches. Les clés sans compte (fondateur et intégration) en sont exemptées.dev_only: une clé sur forfait dev qui utilise une capacité premium depuis une origine de production. La même clé est acceptée depuislocalhost,127.0.0.1,*.localhost,*.pages.devet*.workers.dev, où toutes les capacités sont gratuites. Elle porte les mêmes champscapability,addonetmessage, mais la correction diffère et c'est pourquoi le code diffère : un compte dev n'a aucun abonnement auquel rattacher un complément, donc l'étape est de souscrire. Voir Couches.usage_limit_reached: une clé sur forfait dev au-delà de ses 5 000 appels premium du mois. Le trafic du noyau sur la même clé n'est pas touché et continue de fonctionner : seules les capacités restreintes refusent. Le plafond se lève au début du mois suivant, et un forfait payant le supprime. Porte les mêmes champscapability,addonetmessage.
400: un paramètre de requête mal formé. Coordonnées de tuile invalides,qmanquant sur un appel de géocodage,bboxmal formé,lon/latnon numériques,categoryinconnue,nearetbboxfournis ensemble ou ni l'un ni l'autre sur une recherche par catégorie, plus de quatre contours d'isochrone ou un contour de plus de 120 minutes, code de thème invalide, couche/data/queryinconnue, etc. Le message nomme le paramètre.501:GET /data/querysur une couche tuilée du catalogue (sans table PostGIS). Le message liste les identifiants interrogeables. Voir Couches.404: style inconnu sur/styles/{style}.json, plage de glyphes ou feuille de sprites manquante (texte brut, pas JSON, voir ci-dessus), archive de terrain ou de courbes de niveau non configurée (JSON), un chemin que la passerelle ne sert pas du tout (texte brut404 Not Found), ou une ressource inconnue sur l'API de compte (codenot_found).204: une requête de tuile au-delà du zoom maximal de l'archive, ou pour une tuile qui ne contient aucune donnée. Une réponse vide, pas un échec : MapLibre surzoome depuis la tuile la plus profonde qu'il a déjà.422:/routen'a pas trouvé de chemin entre les deux points fournis. Le corps est{ "error": "no route" }, qu'un502sur le même point de terminaison porte aussi; branchez donc sur le statut. La cause habituelle est la couverture : le graphe se limite au Canada, donc un point hors du Canada, en pleine eau ou non relié au réseau routier (Iqaluit vers le continent) donne un422./matrixet/matchrépondent le même422, avec le même code : une matrice échoue en entier dès qu'une paire est inaccessible ou dépasse la limite de distance routière du moteur, plutôt que de mettrenullsur cette seule cellule, et une trace qu'on ne peut accrocher à aucune route échoue d'un bloc./optimized-routedevrait échouer de la même façon, en entier, puisqu'il résout une matrice en dessous, mais cela n'a pas encore été confirmé contre la passerelle déployée; voir Itinéraires.502: le conteneur de routage ou de géocodage a répondu, mais par un échec qui lui est propre.503: trois situations différentes :-
Le conteneur n'a pas répondu du tout, ou n'a pas répondu dans le délai imparti par la passerelle. C'est la seule que vous puissiez réellement voir en production, pendant un redémarrage de conteneur. Le corps porte un
code, un drapeauretryableet unRetry-After: 5court :{ "error": "geocode temporarily unavailable", "code": "service_unavailable", "retryable": true }Le nom du service est
geocodeourouting. Rien n'est divulgué sur la cause, délibérément : le texte d'erreur d'un conteneur peut nommer une étiquette d'image ou un message de base de données. -
Le service n'est pas branché du tout. C'est ce que vous voyez en exécutant la passerelle localement sans les conteneurs, pas en production. Le corps ne porte pas de
code; il répète plutôt la forme de requête attendue, pour qu'un appelant apprenne le contrat depuis l'échec :{ "error": "routing is not deployed yet", "contract": "?from=lng,lat&to=lng,lat&mode=auto|car|bicycle|pedestrian|truck" } -
Une recherche par catégorie peut retourner
503avec{ "error": "category search requires a corpus rebuilt with categories" }, ce qui signifie que le jeu de données en service précède l'étiquetage par catégorie. Celui-là se règle à la prochaine construction des données.
-
Chaque page d'API liste ses propres messages 400 mot pour mot : l'API de cartes,
Géocodage, Itinéraires.
Les corps, mot pour mot
Les six échecs que vous rencontrerez le plus souvent, exactement tels que la passerelle les envoie. Chacun est copié d'une vraie réponse, non paraphrasé.
Clé manquante, 401. Ni Authorization: Bearer, ni X-API-Key, ni ?key= :
{ "error": "missing API key", "code": "unauthorized" }Clé invalide ou désactivée, 401. La clé est bien formée mais absente du registre, ou elle a
été révoquée. Les deux cas sont délibérément indiscernables de l'extérieur :
{ "error": "invalid or disabled API key", "code": "unauthorized" }Au-delà d'une limite, 429. Quatre situations différentes derrière un seul statut, que le
code distingue. rate_limited est la limite de débit par clé et porte Retry-After: 60 :
{ "error": "rate limit exceeded", "code": "rate_limited" }Les trois autres sont les drapeaux d'application mensuels, posés par la tâche planifiée de cinq
minutes plutôt que comptés à chaque requête. quota_exceeded et spend_cap portent
Retry-After: 3600; billing_required n'en porte aucun, parce qu'aucune attente ne règle un
paiement échoué :
{
"error": "monthly quota exceeded: raise your spend cap or upgrade at unmap.dev/dashboard/billing",
"code": "quota_exceeded",
}
{ "error": "spend cap reached: raise it at unmap.dev/dashboard/billing", "code": "spend_cap" }
{
"error": "payment failed: update your payment method at unmap.dev/dashboard/billing",
"code": "billing_required",
}Mode d'itinéraire non pris en charge, 400. /route et /isochrone valident mode au lieu
de retomber sur une valeur par défaut, donc une faute de frappe est bruyante :
{ "error": "mode must be one of auto, car, bicycle, pedestrian, truck" }Code de thème invalide, 400. Le seul 400 du plan de données qui porte un code, plus un
tableau detail qui dit ce qui n'allait pas. Un code qui n'est ni un document ut1. ni un préréglage
court échoue d'une façon, et un code ut1. dont la charge utile ne se décompresse pas échoue d'une
autre :
{
"error": "invalid theme",
"code": "invalid_theme",
"detail": ["code must start with \"ut1.\" or be a short preset"],
}
{
"error": "invalid theme",
"code": "invalid_theme",
"detail": ["code is not valid base64url deflate data"],
}Style inconnu, 404. /styles/{style}.json énumère les noms qui existent, donc une erreur
se corrige d'elle-même :
{
"error": "unknown style",
"styles": ["base", "muted", "outdoor", "blueprint", "blush", "orchid", "canopy", "lagoon", "tropic", "sunset", "bold", "pastel"],
}Dans le SDK
@unmap/geocoding et @unmap/routing (et donc @unmap/sdk) lèvent une exception sur toute
réponse non 2xx. GeocoderError et RouterError portent le status HTTP et, quand le corps en
avait un, le code lisible par machine ci-dessus; le message error de la passerelle devient le
message de l'exception, donc un appelant qui la journalise lit la raison et non seulement un
nombre. Un corps qui n'est pas du JSON, comme le texte brut d'un chemin de tuiles, laisse
simplement code indéfini. Les tuiles, glyphes et sprites sont récupérés par MapLibre lui-même,
qui signale les échecs par des événements error sur la carte plutôt que par des exceptions.
Réessayer
Seuls 429, 502 et 503 valent un nouvel essai, et seulement avec temporisation. Un 429
indique déjà combien de temps attendre via Retry-After, et un 429 avec
code: "billing_required" ne se règle pas tant que le mode de paiement n'est pas corrigé. Un 503
avec code: "service_unavailable" porte Retry-After: 5 et annonce retryable: true dans le
corps : c'est le seul échec où réessayer rapidement est le bon geste. Un 400, 401, 403, 404
ou 422 décrit quelque chose dans la requête ou le compte qu'un nouvel essai ne corrigera pas;
corrigez la requête.