Leitfaden: Fehler behandeln

Einführung

Dieser Leitfaden behandelt die Fehlerbehandlung bei der Interaktion mit der C2C-API.Die fehlerfreie Verarbeitung von HTTP-Fehlern ist eine wesentliche Komponente einer robusten Integration mit jedem Drittanbieterdienst.

Fehlertypen

Fehler bei Ihrer Integration können aus verschiedenen Quellen stammen, die wir in vier Hauptgruppen einteilen können:

  • I/O-Fehler: Entstehen durch Hardwarevorgänge auf Ihrem Gerät, z. B. fehlgeschlagene Lese-/Schreibvorgänge
  • Anwendungsfehler: Treten aufgrund von Problemen innerhalb Ihres Anwendungscodes auf
  • Netzwerkfehler: Treten innerhalb des Netzwerk-Stacks auf und werden von Ihrer Netzwerkbibliothek kommuniziert
  • API-Fehler: Durch Backend-Services von Frame.io generiert

Für jede Fehlerkategorie sind spezielle Handhabungsüberlegungen erforderlich.Dieser Leitfaden konzentriert sich hauptsächlich auf API-Fehler, aber wir werden auch allgemeine Strategien für die anderen Kategorien behandeln.

Wie API-Fehler zurückgegeben werden

Die API von Frame.io kommuniziert Fehler über zwei primäre Mechanismen:

  • Statuscodes: HTTP-Fehlercodes, die die Art des Problems angeben
  • Fehlermeldungen: Payload-Inhalt, der zusätzliche Fehlerdetails bereitstellt, insbesondere wenn mehrere Fehlerbedingungen denselben Statuscode verwenden

Fehler-Statuscodes

HTTP-Statuscodes sind standardisierte numerische Antworten, die das Ergebnis einer HTTP-Anfrage kommunizieren.Weitere Informationen finden Sie in der Dokumentation zum HTTP-Statuscode von Mozilla oder in HTTP Cats für einen visuelleren Ansatz.Jeder Frame.io-API-Endpunkt gibt einen erwarteten Erfolgs-Statuscode an – in der Regel 200 (OK), 201 (Created) oder 204 (No Content).Sie können den Erfolg überprüfen, indem Sie entweder nach diesen spezifischen Codes suchen oder bestätigen, dass der Code zwischen 200 und 299 liegt.Statuscodes über 399 weisen auf Fehler hin.Die meisten API-Fehler geben 4XX-Codes (400–499) zurück, was auf Client-Probleme hinweisen kann.Fehler außerhalb dieses Bereichs entstehen in der Regel durch die Netzwerkinfrastruktur zwischen Ihrem Gerät und unserem Dienst. Eine Ausnahme ist 500 (Internal Server Error), der auf ein unerwartetes Problem innerhalb unseres Servers hinweist.Ebenso könnte eine 404 (Not Found)-Antwort von Intermediate Services statt von unserem Backend generiert werden, obwohl es sich um einen 4XX-Code handelt.

Wenn unerwartete Statuscodes auftreten, benachrichtigen Sie bitte unser Team.

Fehler bei Payload-Schemas

Frame.io gibt Fehlerdetails in zwei Formaten zurück: simple und detailed.Ihre Fehlerbehandlungslogik sollte beide Formate unterstützen.

Einfaches Fehlerschema

Hier ist ein Beispiel für eine fehlgeschlagene Anfrage mit einem falschen client_secret:

$curl -X POST https://api.frame.io/v2/auth/device/code \
> --include \
> --header 'x-client-version: 2.0.0' \
> --form 'client_id=Some-Client-ID' \
> --form 'client_secret=bad_secret' \
> --form 'scope=asset_create offline'

Antwort:

HTTP/2 400
...
{"error":"invalid_client"}

Das einfache Schema enthält nur ein einzelnes Feld zur Fehlererkennung.

Detailliertes Fehlerschema

Zum Vergleich hier eine Anfrage ohne entsprechende Berechtigung:

$curl -X POST https://api.frame.io/v2/devices/heartbeat \
> --header 'Authorization: Bearer bad-token' \
> --header 'x-client-version: 2.0.0' \
> | python -m json.tool

Antwort:

1{
2 "code": 409,
3 "errors": [
4 {
5 "code": 409,
6 "detail": "The channel you're uploading from is currently paused.",
7 "status": 409,
8 "title": "Channel Paused"
9 }
10 ],
11 "message": "Channel Paused"
12}

Detaillierte Fehler enthalten ein message-Feld, das den Fehlertyp angibt.

Fehlertyp ermitteln

Prüfen Sie bei der Verarbeitung von Frame.io-Fehlern zuerst, ob eine Fehler-Payload vorhanden ist, und fallen Sie dann zurück zum HTTP-Statuscode, wenn keine Payload vorhanden ist.

Hier ist eine einfache Implementierung der Fehlerbehandlung:

Python
1# Dict of known error codes: native errors.
2ERROR_STATUS_MAP = {
3 429: SlowDownError,
4 ...
5}
6
7# Dict of known error messages: native errros.
8ERROR_MESSAGE_MAP = {
9 "Channel Paused": ChannelPausedError,
10 "invalid_client": InvalidClientError,
11 "slow_down": SlowDownError,
12 ...
13}
14
15def _c2c_extract_error_message(response):
16 """
17 Gets the error message from an error payload. Returns `None`
18 if an error payload is not found.
19 """
20
21 # Try to decode the payload, if it is not JSON return `None`
22 try:
23 payload = response.json()
24 except JSONDecodeError:
25 return None
26
27 # Try the simple error schema first.
28 message = payload.get("error", default=None)
29 if message is not None:
30 return message
31
32 # Now try the detailed schema. Return None if we do not find one.
33 return payload.get("message", default=None)
34
35def _c2c_error_type_from_response(response):
36 """
37 Converts a bad HTTP response into an error.
38 """
39 error_message = _c2c_extract_error_message(response)
40
41 # try to do a lookup of the error type by message.
42 error_type = ERROR_MESSAGE_MAP.get(error_message, default=None)
43 if error_type is not None:
44 return error_type()
45
46 # If not, try to do a lookup by error code.
47 error_type = ERROR_STATUS_MAP.get(response.status_code, default=None)
48 if error_type is not None:
49 return error_type()
50
51 # Otherwise we are going to return an `UnknownAPIError` to signal that we
52 # encoutnered an error from Frame.io's backend servers, but do not know the
53 # message and/or status code.
54 return UnknownAPIError(message=error_message)
55
56def raise_on_frameio_error(response, expected_status):
57 """
58 Raises a native error from an HTTP response if the response indicates an error
59 occured. Expected status should be the status we expect to get (200, 201, 204,
60 etc).
61 """
62
63 # If the status code is less than `400`, then it is not an error status code.
64 if response.status < 400:
65
66 # Check that the status code is the one we expected, otherwise raise an
67 # error.
68 if response.status != expected_status:
69 raise UnexpectedStatusError(
70 expected=expected_status, received=response.status
71 )
72
73 return None
74
75 # Otherwise convert and raise a native error.
76 raise _c2c_error_type_from_response(response)

Die in diesem Beispiel erwähnten Fehler-Lookup-Tabellen sind am Ende dieses Leitfadens aufgeführt.

AWS-Fehler

Beim Hochladen von Datei-Chunks interagieren Sie direkt mit AWS S3, das ein eigenes Fehlerformat hat.Weitere Informationen finden Sie in der allgemeinen Fehlerdokumentation von AWS.Generell sollte bei nicht schwerwiegenden AWS-Fehlern der Vorgang mindestens einmal wiederholt werden.

AWS-Fehler werden als XML zurückgegeben:

1<?xml version="1.0" encoding="UTF-8"?>
2<Error>
3 <Code>NoSuchKey</Code>
4 <Message>The resource you requested does not exist</Message>
5 <Resource>/mybucket/myfoto.jpg</Resource>
6 <RequestId>4442587FB7D0A2F9</RequestId>
7</Error>

Das Code-Element gibt den Fehlertyp an.

Vorgänge mit Fehlern wiederholen

Zeitpunkt der Wiederholung

Die Fehlertabellen in diesem Leitfaden geben an, bei welchen API-Fehlern der Vorgang wiederholt werden sollte.Bei Nicht-API-Fehlern durch I/O-Vorgänge, Netzwerkbibliotheken oder AWS sollten Sie versuchen, die Vorgänge mit Fehlern zu wiederholen, die sich aus vorübergehenden Bedingungen ergeben können.Netzwerküberlastung, temporäre Serverausfälle oder Paketverluste erfordern in der Regel Wiederholungsversuche.Die meisten Netzwerkbibliotheken lösen TimeoutError aus, wenn eine Anfrage zu lange dauert – ein idealer Fall für einen erneuten Versuch.

Im Zweifelsfall versuchen Sie es erneut

In Rechenumgebungen können unvorhersehbare Probleme auftreten.Selbst bei fatalen Fehlern lohnt es sich oft, einen erneuten Versuch zu unternehmen.Temporäre Systemzustände, Hardwareanomalien (wie cosmic ray bit flips) oder seltene Speicherzustände können scheinbar schwerwiegende Fehler verursachen, die sich bei einem zweiten Versuch beheben lassen.Bei einigen Fehlern sollte jedoch kein erneuter Versuch unternommen werden.Beispiel : Eine 409: CHANNEL PAUSED-Antwort beim Erstellen eines Assets zeigt an, dass das Gerät angehalten wurde und nicht hochgeladen werden sollte.Dieser Status ist beabsichtigt und wird sich bei einem erneuten Versuch wahrscheinlich nicht ändern.

Exponentieller Backoff

Frame.io implementiert die Geschwindigkeitsbegrenzung, und das Überschreiten dieser Grenzwerte führt entweder zu einem 429: Slow Down-Fehler oder einem 400-Status mit dieser Payload:

HTTP/2 400
{"error":"slow_down"}

Wenn Sie diese Antworten erhalten, implementieren Sie einen exponentiellen Backoff für Wiederholungsversuche.Eine empfohlene Formel zur Berechnung der Verzögerung (in Sekunden) lautet:

Python
1delay = min(2 ** attempt / 2, 32.0)

Wobei der attempt bei 0 beginnt.Dadurch entstehen Verzögerungen von 0,5 s, 1 s, 2 s, 4 s, 8 s, 16 s, 32 s, wobei alle nachfolgenden Versuche 32 Sekunden warten.

Backoff-Jitter

Wir empfehlen, Ihrem Backoff-Timing Zufälligkeit (Jitter) hinzuzufügen, um zu verhindern, dass die Anfragesynchronisierung über mehrere Geräte hinweg aus demselben Fehlerzustand wiederhergestellt wird.Dies trägt dazu bei, das „Thundering-Herd-Problem“ zu entschärfen, bei dem viele Geräte nach einem Ausfall gleichzeitig einen erneuten Verbindungsversuch unternehmen.Ein guter Ansatz besteht darin, einen zufälligen Versatz zwischen 0 und der Hälfte der berechneten Verzögerung hinzuzufügen: math.rand(0, delay // 2).

Der exponentielle Backoff ist zwar für die Begrenzung von Übertragungsraten unerlässlich, erweist sich aber auch allgemein als vorteilhaft bei der Bewältigung von Netzwerk- und E/A-Fehlern.Dieser Ansatz ermöglicht es, vorübergehende Ressourcenengpässe zu beheben, ohne dass Ihre Wiederholungsversuche eine zusätzliche Belastung darstellen.

Den Status „Nicht verbunden“ erkennen

Wenn Netzwerkfehler auftreten, können diese darauf hinweisen, dass Frame.io aus folgenden Gründen nicht erreichbar ist:

  • Ihr lokales Netzwerk ist heruntergefahren
  • Bei den Frame.io-Services sind Probleme aufgetreten
  • Eine zwischengeschaltete Netzwerkkomponente schlägt fehl

Es ist wichtig, diese Bedingungen zu erkennen.Wenn ein Fehler auf Verbindungsprobleme hinweist, implementieren Sie eine Überwachungsaufgabe, die die Wiederherstellung des Dienstes prüft und Benutzende über die Trennung informiert.

Auf Verbindung und Autorisierung warten

Entwickeln Sie Ihre Anwendung so, dass Sie unnötige Anfragen vermeiden, wenn das Gerät eine Autorisierung aktualisiert, auf eine Benutzerautorisierung wartet oder Frame.io nicht erreichen kann.Dies verringert den Netzwerkaufwand und verbessert die Benutzererfahrung.

Blockieren Sie alle API-Aufrufe (mit Ausnahme von https://api.frame.io/health), wenn Sie einen getrennten Zustand erkennen.Wenn Verbindungsprobleme auftreten, starten Sie eine Hintergrundaufgabe, die den Integritätsendpunkt abfragt und weitere API-Aufrufe blockiert, bis die Konnektivität wiederhergestellt ist.

Wenn ein Token abläuft, blockieren Sie autorisierungsabhängige Aufrufe, bis ein neuer Token ausgegeben wird.Wenn die Aktualisierung des Tokens fehlschlägt, weisen Sie den Benutzenden an, sich erneut zu authentifizieren.

Wenden Sie beim Abfragen des Verbindungsstatus denselben exponentiellen Backoff an, der zuvor beschrieben wurde.

Anfrage-Timeouts

Konfigurieren Sie geeignete Timeout-Werte für verschiedene Arten von Anfragen:

  • Standard: 15 Sekunden für grundlegende Anfragen
  • Autorisierung aktualisieren: 2 Minuten, um potenzielle Backend-Verarbeitung zu berücksichtigen
  • Datei-Chunk-Upload: 5 Minuten für langsame Netzwerke bei der Übertragung größerer Daten

Beispiel für einen Wiederholungs-Handler

Hier ist eine Pseudocode-Implementierung, die die Fehlerbehandlung mit exponentiellem Backoff zeigt:

Python
1# List of errors we know are fatal and should not be retried.
2FATAL_ERRORS = (
3 ChannelPausedError,
4 DevicesDisabledError,
5 ...
6)
7
8# List of errors we know should be retried more than once.
9RETRY_ERRORS = (
10 TimeoutError,
11 NotFoundError,
12 SlowDownError,
13 UnknownAPIError,
14 ...
15)
16
17# List of errors that could be the result of Frame.io being unreachable.
18DISCONNECTED_ERRORS = (
19 TimeoutError,
20 HttpClientError,
21 ...
22)
23
24def retry_with_backoff(next_handler):
25 """
26 Middleware for retrying errors with exponential backoff.
27 """
28
29 def retry_handler(call, retry_count):
30 """
31 Handler for retrying c2c API calls with exponential backoff.
32 """
33
34 error = None
35
36 # We will retry the call 8 times here, totalling 63.5 seconds +- ~32 seconds.
37 for attempt in range(start=1, stop=retry_count + 1):
38
39 # If we are attempting to reach an endpoint that requires authorization
40 # we should wait unil we have valid authorization before attempting
41 # a call. We need to do this each time in case our access_token
42 # expires between attempts.
43 C2C.wait_for_authorized(call)
44
45 # Likewise, we should wait until we are connected to Frame.io to attempt
46 # a call if we are not calling `https://api.frame.io/health`
47 C2C.wait_for_connected(call)
48
49 try:
50 # Return the result on a success.
51 return next_handler(call)
52 except FATAL_ERRORS as error:
53 # If we hit an error we know is fatal, raise the error without
54 # retrying it.
55 raise error
56
57 except RETRY_ERRORS as error:
58 # If we hit an error we know we should retry many times, continue,
59 # but notify our client if we think we may have been disconnected.
60 if type(error) in DISCONNECTED_ERRORS:
61 C2C.notify_disconnected()
62
63 except BaseException as error:
64 # Otherwise, do not retry the call more than once.
65 if attempt > 1:
66 raise error
67
68 # The delay for the next attempt should be no more than 32 seconds.
69 # This algorithm will go: 0.5s, 1s, 2s, 4s, 8s, 16s, 32s, 32s, ...
70 delay = min(2 ** attempt / 2, 32.0)
71
72 # Add some randomness (jitter) to the delay (up to half the value of
73 # the delay in either direction).
74 delay += math.random(-delay, delay) / 2
75
76 # Wait between retries
77 sleep(delay)
78
79 # If we have exhausted all retries,
80 raise error
81
82 return retry_handler

Fehlertabellen

In den folgenden Tabellen werden Frame.io-API-Fehler kategorisiert und Hinweise zur Handhabung gegeben.Die einzelnen Spalten stellen Folgendes dar:

message: Die Kennung der Fehlernachricht http code: Der HTTP-Statuscode error type: Eine konzeptionelle Fehlerkategorie (ausführlich im Abschnitt Beschreibungen erläutert) schema: Das Format der Fehlernachricht (simple oder detailed) retry: Empfehlung für einen erneuten Versuch (yes für mehrere Versuche, once für einen einzelnen Versuch, no für schwerwiegende Fehler) Sternchen (*) kennzeichnen besondere Hinweise, die im Abschnitt Beschreibungen näher erläutert werden.

Frame.io-Fehlermeldungen

MeldungFehlertypHTTP-CodeSchemaretry
”access_denied”AccessDenied401simpleonce
”authorization_pending”AuthorizationPending400simpleyes
”Channel Paused”ChannelPaused409simpleno
”expired_token”ExpiredToken400simpleno
”Invalid Argument”InvalidArgument422detailedno
”invalid_client”InvalidClient400simpleno
”Invalid client version”InvalidClientVersion400simpleno
”invalid_grant”InvalidGrant400simpleno
”invalid_request”InvalidRequest400simpleonce
”Not Authorized”UnauthorizedClient401detailedno
”slow_down”SlowDown400simpleyes
”unauthorized_client”UnauthorizedClient401simpleyes*

Frame.io-Statuscodes

HTTP-CodeFehlertypretry
400InvalidRequestonce
401UnauthorizedClientno
422InvalidContentTypeno
429SlowDownyes
500InternalServerErroryes

AWS-Fehler

In der AWS-Dokumentation erhalten Sie weitere Informationen.

Fehlerretry
InternalErroryes
OperationAbortedyes
RequestTimeoutyes
ServiceUnavailableyes
SlowDownyes
[All Other Errors]once
Ähnliche AWS-Fehler parsen

Sowohl SlowDown als auch ServiceUnavailable von AWS deuten auf Probleme mit der Anfragerate hin und können ähnlich wie der SlowDown-Fehler von Frame.io behandelt werden, indem ein exponentieller Backoff implementiert wird.Ebenso entspricht der AWS-Fehler InternalError konzeptionell dem Fehler InternalServerError in unserer API.

Beschreibungen

AccessDenied

Wird zurückgegeben, wenn die Autorisierung während der Gerätekopplung abgelehnt wird.

AuthorizationPending

Zeigt an, dass jemand den Kopplungscode des Geräts noch nicht eingegeben hat.Setzen Sie die Abfrage nach Ablauf des in der Antwort des Gerätecodes angegebenen interval fort.

ChannelPaused

Der Gerätekanal wurde beim Erstellen des Assets angehalten.Versuchen Sie nicht, dieses Asset erneut hochzuladen.

ExpiredToken

Der Kopplungscode des Geräts ist abgelaufen.Erstellen Sie einen neuen Code und starten Sie den Kopplungsprozess erneut.

InternalServerError

Gibt ein unerwartetes Backend-Problem an.Versuchen Sie es erneut, und melden Sie 500-Fehler unserem Team zur Untersuchung.Beachten Sie, dass einige bekannte Probleme 500-Fehler zurückgeben, obwohl sie InvalidRequest zurückgeben sollen:

  • Es wird versucht, in einen nicht vorhandenen Gerätekanal hochzuladen
  • Ungültige selbstdefinierte Chunk-Anzahl wird angefordert

InvalidArgument

Ein Payload-Parameter enthielt einen ungültigen Wert.Prüfen Sie, ob die Parameterwerte den API-Erwartungen entsprechen.

InvalidContentType

Der Content-Type-Header der Anfrage wird nicht unterstützt.Die API akzeptiert im Allgemeinen:

  • form/multipart (nur Autorisierungsendpunkte)
  • application/x-www-form-urlencoded (alle Endpunkte)
  • application/json (Nicht-Autorisierungs-Endpunkte)

InvalidClient

Die angegebenen Anmeldedaten (client_id, client_secret usw.)wurden nicht erkannt.Überprüfen Sie Ihre Integrationsanmeldedaten.

InvalidClientVersion

Der Header x-client-version wurde entweder dupliziert oder enthält eine ungültige semantische Version.

InvalidGrant

Der Autorisierungserteilungstyp ist ungültig.Schauen Sie in den Autorisierungsleitfäden nach den korrekten Werten.

InvalidRequest

Die Anfrageparameter oder das Payload-Format sind falsch.Prüfen Sie die Feldnamen und Wertformate.

Wenn diese Meldung während der Aktualisierung des Tokens angezeigt wird, ist Ihr Aktualisierungstoken abgelaufen, und Sie müssen den Autorisierungsprozess neu starten.

SlowDown

Sie haben die Grenzwerte für die Anfragerate überschritten.Implementieren Sie einen exponentiellen Backoff für nachfolgende Anfragen.Beachten Sie, dass mehrere Gerätecode-Anfragen über dieselbe TCP-Verbindung diesen Fehler auslösen können – stellen Sie für jede Kopplungsanfrage eine neue Verbindung her.

UnauthorizedClient

Gibt in der Regel einen abgelaufenen oder fehlenden access_token an.Wenn Sie diesen Fehler erhalten, aktualisieren Sie Ihren Token, bevor Sie es erneut versuchen.

Wenn während der Tokenaktualisierung ein Fehler auftritt, müssen Sie den Autorisierungsprozess neu starten und Benutzende auffordern, die Verbindung wiederherzustellen.

Dieser Fehler kann auch auftreten, wenn auf Ressourcen außerhalb des Autorisierungsgültigkeitsbereichs Ihres Geräts zugegriffen wird oder wenn für ein Projekt C2C-Geräte deaktiviert sind.Vergewissern Sie sich, dass Sie bei der Autorisierung die entsprechenden Gültigkeitsbereiche angefordert haben.

Tritt dieser Fehler während der Token-Aktualisierung auf, muss der gesamte Autorisierungsprozess durch Benutzende neu gestartet werden.

Nächste Schritte

Wir empfehlen Ihnen, sich bei Fragen an unser Team zu wenden und mit dem Leitfaden zu erweiterten Uploads fortzufahren.Wir freuen uns darauf, Ihren Integrationsprozess zu unterstützen.Wenn Sie es noch nicht getan haben, lesen Sie bitte den Leitfaden C2C implementieren: Einrichtung, bevor Sie fortfahren.Sie benötigen den access_token, den Sie während des Authentifizierungs- und Autorisierungsprozesses erhalten haben.Dieser Leitfaden baut auf dem Leitfaden zu einfachen Uploads und dem Leitfaden zu erweiterten Uploads auf.