Fehler
Jeder Fehler der API ist ein Problem nach RFC 9457 mit Content-Type: application/problem+json; charset=utf-8. Dieses hier stammt von einem synchronen Rendering, dessen Vorlage sich nicht kompilieren lässt:
{
"type": "https://docs.formfeed.dev/errors/template-syntax",
"title": "Template syntax error",
"status": 422,
"detail": "unknown block tag: endfo",
"code": "template_syntax_error",
"location": { "line": 41, "column": 3 },
"render_id": "rnd_7da09ab6cd9946b798c187c2a9350852",
"request_id": "req_4b7c1e9a2f3d4c5b8a6e7f9012"
}
Felder
Jedes Problem hat diese Felder:
| Feld | Bedeutung |
|---|---|
code | Stabiler, maschinenlesbarer Fehlercode. Verzweigen Sie anhand dieses Felds, nicht anhand von title oder detail. |
status | Der HTTP-Status, im Body wiederholt. Er folgt immer aus dem Code, wie die Tabelle unten zeigt. |
title | Kurzbeschreibung des Codes; für jedes Problem mit diesem Code gleich. |
detail | Was bei dieser Anfrage schiefgelaufen ist, für Menschen und Logs. Der Wortlaut kann sich ändern; werten Sie ihn nie maschinell aus. |
type | Ein Link auf die Seite zu diesem Code. |
request_id | Die ID dieser Anfrage, req_ gefolgt von 26 Hex-Ziffern. Derselbe Wert steht im Antwort-Header X-Request-Id. Senden Sie einen eigenen Header X-Request-Id (bis zu 255 Buchstaben, Ziffern, _, - und =), dann verwendet Formfeed diesen. Geben Sie sie an, wenn Sie den Support kontaktieren. |
Viele Probleme ergänzen eigene Felder:
| Feld | Wo |
|---|---|
issues | invalid_request, wenn Body oder Query nicht zum API-Vertrag passen: ein Eintrag pro Problem, mit path (dem Feld, etwa mode oder post.watermark.text; leer für den Body als Ganzes) und message. |
errors | data_validation_error und einvoice_data_invalid: jede Stelle, an der die Daten nicht passen, mit path, message und bei E-Rechnungen der rule. |
location | Bei den Problemen eines Renderings, das während der Ausführung fehlgeschlagen ist. Bei einem Vorlagenfehler: line und column in dem Teil, der fehlschlug (Body, Kopf- oder Fußzeile; das Problem sagt nicht, welcher). Bei einer Word- oder PowerPoint-Vorlage: part (etwa word/document.xml) und, soweit bekannt, paragraph. null, wenn der Fehler keine Stelle in der Vorlage hat. |
field | Das Feld der Anfrage, um das es geht, etwa output, validate_data, post.merge_after oder storage.key. |
index | In einem Batch das fehlgeschlagene Element (ab 0 gezählt). Bei einem PDF-Tool die Position der Quelle in sources. |
render_id | Die rnd_…-ID des Renderings, um das es geht: ein synchrones Rendering oder ein PDF-Tool, das nach seiner Aufzeichnung fehlgeschlagen ist und das dann das Render-Protokoll und GET /v1/renders/{id} zeigen, oder das Quell-Rendering, das ein PDF-Tool nicht verwenden konnte. |
feature, plan, limit | forbidden, wenn der Plan etwas nicht enthält: welche Funktion, der Plan und die Anzahl, die der Plan erlaubt. |
einvoice, accessibility | Die Berichte des Validators zu einer E-Rechnung oder einer PDF/UA-1-Datei, die durchgefallen ist. |
Manche Codes ergänzen weitere Felder, etwa page_count bei page_limit_exceeded oder period_end bei quota_exceeded; die Seite des jeweiligen Codes führt sie auf.
Fehlgeschlagene asynchrone Renderings
Ein asynchrones Rendering, ein Batch-Element und ein synchrones Rendering, das im Hintergrund weiterlief (eine Antwort 202), schlagen erst später fehl, die Anfrage liefert also kein Problem zurück. Der Fehler bleibt stattdessen am Rendering:
GET /v1/renders/{id}und der Webhookrender.failedtragen ihn inerror, in kürzerer Form:code,message(wasdetailsagen würde) und die Felder des Codes, etwalineundcolumnoderpage_count. Die Berichte zu E-Rechnung und PDF/UA stehen in den eigenen Felderneinvoiceundaccessibilitydes Renderings.GET /v1/renders/{id}/resultbeantwortet ein fehlgeschlagenes Rendering mit dem vollständigen Problem, wie es die synchrone Anfrage getan hätte, einschließlichrender_id.
Ein fehlgeschlagenes Rendering, seine übrigen Felder weggelassen:
{
"id": "rnd_7da09ab6cd9946b798c187c2a9350852",
"status": "failed",
"error": {
"code": "page_limit_exceeded",
"message": "Document has 212 pages; the plan allows 200",
"page_count": 212
}
}
Ein Rendering, das dreimal auf Seiten von Formfeed fehlgeschlagen ist, hat "code": "worker_exhausted", die message des letzten Versuchs und attempts; siehe Wiederholen.
Fehler im Code behandeln
Die SDKs machen aus jedem Problem eine Fehlerklasse. TypeScript:
import { Formfeed, FormfeedError } from '@formfeed/sdk';
const client = new Formfeed({ apiKey: process.env.FORMFEED_API_KEY! });
try {
await client.renders.create({ template: 'invoice', data });
} catch (e) {
if (!(e instanceof FormfeedError)) throw e;
switch (e.code) {
case 'template_syntax_error':
case 'template_runtime_error':
// ein Fehler in der Vorlage: die verantwortliche Person benachrichtigen, nicht wiederholen
console.error('template error', e.message, e.problem, e.requestId);
break;
case 'quota_exceeded':
// keine Live-Renderings mehr senden, bis die Periode neu beginnt oder der Plan wechselt
break;
default:
throw e;
}
}
Python:
import os
from formfeed import Formfeed, FormfeedError
client = Formfeed(os.environ["FORMFEED_API_KEY"])
try:
client.renders.create(template="invoice", data=data)
except FormfeedError as e:
if e.code in ("template_syntax_error", "template_runtime_error"):
# ein Fehler in der Vorlage: die verantwortliche Person benachrichtigen, nicht wiederholen
print("template error", e.message, e.problem, e.request_id)
elif e.code == "quota_exceeded":
pass # keine Live-Renderings mehr senden, bis die Periode neu beginnt oder der Plan wechselt
else:
raise
TypeScript (FormfeedError) | Python (FormfeedError) | Bedeutung |
|---|---|---|
code | code | Der code des Problems oder ein eigener Code des SDK (siehe unten) |
status | status | Der HTTP-Status; 0, wenn keine Antwort ankam |
message | message | Das detail des Problems, sonst sein title |
problem | problem | Der ganze Body des Problems mit allen Feldern oben; null bzw. None bei einem Fehler des SDK |
requestId | request_id | Der Header X-Request-Id der Antwort |
Codes, die die SDKs selbst auslösen, ohne Problem der API: network_error (keine Antwort nach den Wiederholungen, eine Zeitüberschreitung der Anfrage eingeschlossen), timeout (nur von waitFor bzw. wait_for, das das Warten auf ein Rendering oder einen Job aufgegeben hat), not_ready (Download eines Renderings ohne Datei), download_failed, invalid_response (TypeScript: eine Erfolgsantwort, die kein JSON ist) und http_<status> für eine Fehlerantwort ohne code.
renders.waitFor bzw. wait_for löst bei einem fehlgeschlagenen Rendering keinen Fehler aus: Es gibt das Rendering mit status: "failed" zurück, und sein error sagt, warum.
Wiederholen
Die SDKs wiederholen 429, 503 und Netzwerkfehler selbst: bis zu 3-mal (maxRetries bzw. max_retries, 0 schaltet es ab), wobei sie die Sekunden aus Retry-After abwarten, wenn die Antwort den Header hat, sonst 0,5, 1 und 2 Sekunden, jeweils plus bis zu 0,25 Sekunden Zufall und nie mehr als 30 Sekunden am Stück. Eine Anfrage, die innerhalb der Zeitgrenze des Clients (timeoutMs bzw. timeout, standardmäßig 120 Sekunden) keine Antwort erhält, zählt als Netzwerkfehler. Jeder Aufruf, der ein Rendering erzeugt (Renderings, Batches, PDF-Tools), sendet einen erzeugten Idempotency-Key, und seine Wiederholungen verwenden ihn erneut. 500, 502 und 504 wiederholen die SDKs nie. Siehe die SDK-Seiten zu TypeScript und Python.
Ohne SDK, oder für das, was die SDKs Ihnen überlassen:
| Antwort | Wiederholen? | Wie |
|---|---|---|
429 rate_limited | ja | Nach den Sekunden aus Retry-After, mit demselben Idempotency-Key. |
503 region_unavailable, office_unavailable, conformance_unavailable | ja | Nach den Sekunden aus Retry-After, mit demselben Idempotency-Key. |
500 worker_exhausted, tool_failed | ja, einige Male | Mit wachsenden Pausen (etwa 5, 30 und 120 Sekunden) und demselben Idempotency-Key. Scheitert es weiter, senden Sie die request_id an den Support. |
500 pdfa_conversion_failed | nein | Dasselbe Dokument scheitert auf dieselbe Weise; senden Sie die render_id an den Support. |
504 render_timeout | nur, wenn die Ursache vorbei ist | Ein langsamer Server für Bilder oder Schriften kann sich erholen; eine zu langsame Vorlage oder Seite scheitert wieder. Siehe die Seite des Codes. |
| Gar keine Antwort (Zeitüberschreitung, abgebrochene Verbindung) | ja | Mit demselben Idempotency-Key: Ist der erste Versuch doch angekommen, erhalten Sie dessen Antwort statt eines zweiten Renderings. |
Jede andere 4xx, und 501 | nein | Beheben Sie, was das Problem nennt, und senden Sie die Anfrage dann mit einem neuen Idempotency-Key oder ohne. |
Warum ein neuer Schlüssel nach einer 4xx: Die erste Antwort auf einen POST mit Idempotency-Key wird 24 Stunden pro Workspace gespeichert und bei jeder Wiederholung dieses Schlüssels erneut geliefert, mit Idempotent-Replayed: true. Das gilt auch für Probleme. Nur Antworten ab 500 werden nicht gespeichert, ebenso wenig eine 401 oder eine 429 des Rate-Limits, die beantwortet werden, bevor der Schlüssel betrachtet wird. Eine korrigierte Anfrage mit dem alten Schlüssel erhält also das alte Problem zurück.
Asynchrone Renderings und Batch-Elemente wiederholt die Warteschlange selbst, wenn sie auf Seiten von Formfeed fehlschlagen (ein Status ab 500, außer pdfa_conversion_failed): drei Versuche, der zweite 5 Sekunden und der dritte 10 Sekunden nach dem vorigen. Schlägt auch der dritte fehl, endet das Rendering mit worker_exhausted und behält die message des letzten Versuchs. Jeder andere Fehler beendet das Rendering sofort. Um ein fehlgeschlagenes asynchrones Rendering selbst erneut zu versuchen, senden Sie die Anfrage mit einem neuen Idempotency-Key: Der alte Schlüssel liefert die gespeicherte 202 des fehlgeschlagenen Renderings.
Ein fehlgeschlagenes Rendering kostet nichts, gleich mit welchem Code.
Alle Codes
| HTTP | Code | Bedeutung | Wiederholen |
|---|---|---|---|
| 400 | invalid_request | Body oder ein Parameter passen nicht zum API-Vertrag, oder eine Kombination von Feldern ist nicht erlaubt. | nein |
| 401 | unauthorized | Die Anfrage enthält keinen API-Schlüssel, oder der Schlüssel ist unbekannt, widerrufen oder abgelaufen. | nein |
| 402 | quota_exceeded | Die Einheiten der aktuellen Periode sind aufgebraucht und der Plan erlaubt keinen Mehrverbrauch, oder der Mehrverbrauch hat seine Obergrenze erreicht. | nein |
| 403 | forbidden | Dem Schlüssel fehlt der Scope, den dieser Endpunkt braucht, dem Plan fehlt die Funktion, oder der Aufrufer darf das nicht. | nein |
| 403 | test_output_unavailable | Ein Test-Rendering im Free-Plan hat eine Word- oder PowerPoint-Vorlage nach ihrem eigenen Format gefragt; es erzeugt nur das markierte PDF. | nein |
| 404 | not_found | Das Rendering, der Job, die Datei, der Webhook, das Partial oder der Pfad existiert in diesem Workspace nicht. | nein |
| 404 | template_not_found | Keine Vorlage mit diesem Slug oder dieser ID hat im Workspace des Schlüssels die Version, die die Anfrage nennt. | nein |
| 404 | channel_not_found | Die Anfrage nennt einen Release Channel, den die Vorlage nicht hat. | nein |
| 409 | conflict | Ein Slug ist vergeben, die Vorlage oder das geteilte Partial hat sich seit Ihrer Basisversion geändert, oder ein Gerätecode wurde schon verwendet. | nein |
| 409 | schema_breaking_change | Veröffentlichen oder Verschieben eines Channels würde eine Version live schalten, deren gespeichertes Datenschema Aufrufer der aktuellen bricht. | nein |
| 409 | channel_in_use | Renderings der letzten Stunde haben den Channel verwendet, den Sie löschen; übergeben Sie force=true. | nach einer Stunde ohne Renderings |
| 409 | listen_session_limit | Im Workspace laufen bereits drei Sitzungen von formfeed listen. | sobald eine endet |
| 409 | last_workspace | Der Workspace, den Sie löschen, ist der letzte der Organisation. | nein |
| 409 | storage_not_configured | Die Anfrage braucht den eigenen Bucket des Workspace (storage.key), aber der Workspace hat keine Speicherverbindung. | nach dem Verbinden eines Buckets |
| 410 | render_input_expired | Die Anfrage des Renderings wurde nicht aufbewahrt, oder ihre Aufbewahrungsfrist ist abgelaufen. | nein |
| 410 | listen_session_ended | Die Sitzung von formfeed listen, die ein erneutes Senden oder eine Zustellung nennt, gibt es nicht mehr. | nein |
| 413 | file_too_large | Eine hochgeladene Datei ist über ihrem Limit: 10 MB für die Dateibibliothek, 20 MB für ein Dokument zum Umwandeln oder eine Word- oder PowerPoint-Vorlage. | nein |
| 415 | file_type_unsupported | Die Datei ist kein Office-Dokument, das Formfeed umwandelt, enthält Makros, oder sie ist nicht die Art, die die Vorlage nimmt. | nein |
| 422 | channel_name_invalid | Der Name eines Release Channels ist fehlerhaft oder reserviert. | nein |
| 422 | template_syntax_error | Die Vorlage ließ sich nicht kompilieren. | nein |
| 422 | template_runtime_error | Die Vorlage wurde kompiliert, ist aber bei der Ausführung mit Ihren Daten fehlgeschlagen. | nein |
| 422 | data_validation_error | Die Daten passen nicht zum Datenschema der Vorlage (bei validate_data: true). | nein |
| 422 | navigation_failed | Ein url-Rendering konnte die Seite nicht laden. | nein |
| 422 | page_limit_exceeded | Das PDF hat mehr Seiten, als der Plan erlaubt. | nein |
| 422 | output_too_large | Die gerenderte Datei oder ihr HTML ist größer als das Limit. | nein |
| 422 | source_not_found | Eine Quelle eines PDF-Tools oder von post.merge_after existiert nicht, oder ihre Ausgabe ist nicht mehr vorhanden. | nein |
| 422 | source_invalid | Eine Quelle ist kein fertiges, lesbares PDF (bei einer Umwandlung: kein lesbares Office-Dokument). | nein |
| 422 | source_encrypted | Eine Quelle ist passwortgeschützt. | nein |
| 422 | office_document_invalid | Der Container des Office-Dokuments ist beschädigt oder besteht eine Sicherheitsprüfung nicht. | nein |
| 422 | office_document_too_large | Das Office-Dokument entpackt sich zu mehr, als Formfeed verarbeitet, oder eine befüllte Vorlage wird zu groß. | nein |
| 422 | office_conversion_failed | LibreOffice konnte das Dokument nicht umwandeln. | nein |
| 422 | unsupported_in_office | Eine Word- oder PowerPoint-Vorlage verwendet etwas, das Office-Vorlagen nicht ausführen können, etwa ein Include. | nein |
| 422 | einvoice_data_invalid | Der _invoice-Block einer E-Rechnung fehlt oder verletzt eine Regel, oder der Validator hat die fertige Datei abgelehnt. | nein |
| 422 | einvoice_profile_unsupported | Die Optionen der E-Rechnung nennen ein Profil oder ein flavour, das Formfeed nicht erzeugt. | nein |
| 422 | einvoice_display_mismatch | Mit display_check: "strict" zeigt das PDF einen Wert nicht, den das XML seiner E-Rechnung nennt. | nein |
| 422 | pdfua_validation_failed | Mit pdf.ua und strenger Prüfung verletzt die Datei eine Regel von PDF/UA-1; accessibility nennt die Regeln. | nein |
| 422 | storage_test_failed | Das Testobjekt ließ sich nicht in den Bucket schreiben, die Speicherverbindung wurde deshalb nicht gespeichert; error enthält den Code des Anbieters. | nach Korrektur von Schlüssel oder Bucket |
| 429 | rate_limited | Zu viele Anfragen, die Arbeit auslösen; warten Sie die Zeit aus Retry-After ab. | ja |
| 500 | tool_failed | Ein PDF-Tool oder die Validierung einer PDF/UA-Datei ist innerhalb von Formfeed fehlgeschlagen. | einmal |
| 500 | pdfa_conversion_failed | Das PDF ließ sich nicht in ein gültiges PDF/A-3b umwandeln, das eine E-Rechnung sein muss. | nein |
| 500 | worker_exhausted | Ein Fehler auf Seiten von Formfeed, den die Wiederholungen nicht behoben haben, oder ein unerwarteter Fehler. | ja |
| 501 | not_implemented | Reserviert für eine dokumentierte Funktion, die noch nicht verfügbar ist; heute antwortet nichts damit. | nein |
| 503 | region_unavailable | Die Region hat gerade keinen freien Render-Platz, oder gar keinen Render-Dienst. | nach Retry-After |
| 503 | office_unavailable | Der Office-Konverter ist ausgelastet oder nicht erreichbar. | ja |
| 503 | conformance_unavailable | Der Dienst, der E-Rechnungen umwandelt und validiert und PDF/UA-Dateien validiert, ist ausgelastet oder nicht erreichbar. | ja |
| 504 | render_timeout | Ein Schritt des Renderings hat länger gedauert als sein Limit. | nur, wenn die Ursache vorbei ist |
Innerhalb von /v1 können neue Codes hinzukommen. Behandeln Sie einen unbekannten Code anhand seines HTTP-Status.