GunSpec
Praxis

Fehler bewusst behandeln

Manche Fehler lohnen einen zweiten Versuch, andere können nur wieder scheitern. Sie zu unterscheiden trennt eine langsame Minute von einem verbrannten Kontingent.

Verzweigen Sie über error.reason, nie über die Meldung: Gründe sind stabil und werden nie umbenannt, Meldungen sind für Menschen geschrieben. Die Fehlerseite listet jeden davon.

GrundStatusWie lange warten
RATE_LIMITED429Retry-After nennt 60 s. Richten Sie sich nach dem Header, nicht nach der Zahl.
PAGINATION_BURST429Exponentielles Backoff mit Jitter.
DAILY_CAP_EXCEEDED429Retry-After nennt 3600 s. Richten Sie sich nach dem Header, nicht nach der Zahl.
DEPENDENCY_UNAVAILABLE503Exponentielles Backoff mit Jitter.
MAINTENANCE503Exponentielles Backoff mit Jitter.
INTERNAL_ERROR500Exponentielles Backoff mit Jitter.

Das sind Antworten, keine Störungen. Sie zu wiederholen kostet Anfragen für denselben Satz, und in einer Schleife erreicht ein Schlüssel so bis mittags sein Tageslimit.

GrundStatusWas tatsächlich hilft
KEY_INVALID401Check the key for a typo, or create a new one in your account.
KEY_DISABLED401Re-enable it in your account, or use another key.
KEY_EXPIRED401Create a new key in your account.
PLAN_REQUIRED403Upgrade the plan. A new key on the same plan will get the same answer.
ACCOUNT_SUSPENDED403Contact support. Rotating the key will not help.
PAGINATION_DEPTH_EXCEEDED403Narrow the list with filters, or upgrade for deeper paging.
INVALID_PARAMETER400Correct the named fields and send the request again.
RESOURCE_NOT_FOUND404Check the id. The list and search endpoints return valid ones.

401 betrifft die Zugangsdaten, 403 das Erlaubte. Ein neuer Schlüssel behebt das Erste und ändert am Zweiten nichts. Authentifizierung zieht die Linie vollständig.

Drei Regeln: nur wiederholbare Gründe, das serverseitige Retry-After wo gesendet, und Jitter, damit eine Flotte, die gemeinsam gescheitert ist, nicht gemeinsam wiederholt.

call.ts
javascript
// Retry the failures that can succeed later, and only those.const RETRYABLE = new Set(['RATE_LIMITED', 'PAGINATION_BURST', 'DAILY_CAP_EXCEEDED', 'DEPENDENCY_UNAVAILABLE', 'MAINTENANCE', 'INTERNAL_ERROR']) async function call(request, attempt = 0) {  const res = await fetch(request)  if (res.ok) return res   const body = await res.json().catch(() => null)  const reason = body?.error?.reason   // A reason the API says is permanent. Sending it again is a  // request spent to receive the same sentence.  if (!RETRYABLE.has(reason) || attempt >= 4) throw new GunSpecError(res.status, body)   // Honour the server's own number when it sends one: it knows when  // the window resets and a guess does not.  const retryAfter = Number(res.headers.get('Retry-After')) || 0  const backoff = retryAfter * 1000 || 2 ** attempt * 500   // Jitter, or every client that failed together retries together.  await sleep(backoff + Math.random() * 250)  return call(request, attempt + 1)}

Die offiziellen SDKs tun das bereits: maxRetries steht auf 2, Retry-After wird beachtet, und jeder Fehler trägt reason und requestId. Stellen Sie es über retry am Client ein, statt es zu umwickeln. Siehe die SDK-Referenz.

Eine Seite, die ohne unsere Daten rendert, ist besser als eine, die gar nicht rendert. Entscheiden Sie das einmal an der Grenze, nicht in jeder Komponente.

firearm-for-page.ts
javascript
// What the reader sees while this is going wrong.try {  return await gunspec.firearms.get(id)} catch (err) {  // A copy from ten minutes ago is a better page than an error, and  // the catalog changes on the order of days.  const stale = await cache.get(id)  if (stale) return { ...stale, stale: true }   // Nothing cached. Say what failed, quote the request id, and let  // the page render the rest of itself.  logger.warn({ requestId: err.requestId, reason: err.reason }, 'gunspec unavailable')  return null}
  • Liefern Sie eine veraltete Kopie, bevor Sie einen Fehler liefern. Der Katalog bewegt sich in Tagen, also sind zehn Minuten alte Daten keine Lüge.
  • Lassen Sie einen Abschnitt scheitern, nicht die Seite: nicht erreichbare Händler dürfen die Datentabelle nicht mitnehmen.
  • Setzen Sie jedem Aufruf ein Timeout. Eine Anfrage, die nie zurückkehrt, ist schlimmer als eine, die scheitert, weil danach nichts mehr entscheiden kann.
  • Wiederholen Sie einen Schreibvorgang nie nur wegen einer langsamen Antwort, solange Sie keine Idempotenz haben. Ein Duplikat ist ein echter Schaden, eine Verzögerung nicht.

Jede Antwort trägt X-Request-Id, und jeder SDK-Fehler trägt sie als requestId. Protokollieren Sie sie. Damit finden wir genau diese Anfrage; ohne sie ist eine Meldung die Beschreibung einer Seite. Support und Feedback ist der Weg dorthin.