Zum Inhalt springen
senn-techsenn-tech
Zurück zum Blog
KI & Entwicklung
KI im Betrieb2026-11-05· Von Franz Senn

Grün, aber leer: Wenn eine Automation ohne Fehler nichts liefert

Auf einen Blick. Ein Statuscode 200 sagt, dass jemand geantwortet hat, und nichts über den Inhalt. Geprüft werden sollte deshalb an jeder Stelle, an der ein Ergebnis weiterverarbeitet wird: Form, Menge und Alter. Sechs Fälle aus unseren Betriebsnotizen von Juli bis September 2026 zeigen, was ohne diese Prüfungen passiert.

Der n8n-Blog beschreibt am Beispiel einer Zahlungs-API, wie ein Lauf grün bleibt und trotzdem falsch rechnet: Der Betrag kommt eines Tages als Text statt als Zahl. Die Anfrage gelingt, der Statuscode stimmt, und erst die Weiterverarbeitung rechnet falsch oder bricht ab. Das Muster kennen wir aus unserem eigenen Betrieb, nur ist es dort meist ein Sprachmodell, ein Suchdienst oder ein Archivierungsjob und keine Zahlungs-API.

Dieser Beitrag ist unabhängig vom Werkzeug gemeint. Wie n8n aufgebaut ist und was es im Eigenbetrieb kostet, steht im Einführungsbeitrag zu n8n. Hier geht es um die Frage danach: Woran merkt man, dass ein grüner Lauf nichts geliefert hat?

Was ein stiller Fehler ist

Ein stiller Fehler ist ein Lauf, der technisch gelingt und fachlich nichts liefert. Der Statuscode ist 200, der Job meldet Erfolg, aber das Feld ist leer, die Liste hat null Einträge oder der Wert hat einen anderen Typ als erwartet. Klassische Überwachung beantwortet, ob etwas läuft, und lässt offen, ob das Ergebnis brauchbar ist.

Eine ähnliche Lücke haben wir schon bei der Überwachung unserer DNS-Resolver beschrieben: Erreichbarkeit ist nicht Gesundheit, nachzulesen im Beitrag Warum ein grüner Uptime-Check nichts beweist. Hier liegt die Lücke eine Ebene höher, im Inhalt der Antwort.

Was der n8n-Blog dazu sagt

Der Beitrag von n8n vom 1. Oktober 2026 nennt drei Änderungen, die gültige HTTP-Antworten liefern und trotzdem die Geschäftslogik brechen: Ein JSON-Objekt wird in ein Array verpackt, ein Aufzählungswert ändert sich und der Ablauf nimmt den falschen Zweig, ein Name wird in zwei Felder geteilt. Sein Rezept: die Antwort direkt hinter dem Abruf gegen ein erwartetes Schema prüfen, Verstöße sofort an Menschen melden und jede Prüfung in der Ausführungshistorie aufbewahren. Dazu gehören laut n8n versionierte Verträge und die Entscheidung, ob jede Anfrage oder nur eine geplante Stichprobe geprüft wird.

Logo von n8n
n8n hat den Blogbeitrag veröffentlicht, auf den sich dieser Text bezieht. (Quelle: Simple Icons 16.36.0, Datei n8n.svg (Repo CC0, im Eintrag kein Lizenzfeld), Marke: n8n)

Zwei Einordnungen. Der Beitrag stammt vom Hersteller und endet mit der Werbung für das eigene Produkt; die Ideen darin sind aber nicht an n8n gebunden, das schreibt der Beitrag selbst. Und der Begriff stammt aus dem Consumer-Driven Contract Testing: Die Pact-Dokumentation beschreibt, dass dabei nur die Teile einer Schnittstelle geprüft werden, die der Verbraucher tatsächlich nutzt.

Sechs Fälle aus unserem Betrieb

Alle Zeilen stammen aus unseren eigenen Betriebsnotizen. Die Zahlen sind eigene Messungen an unserem LiteLLM-Gateway und den genannten Systemen, die Modelle stehen unter der Tabelle. Diese Aufstellung ist eine Zusammenfassung dieser Notizen und keine neue Messreihe.

DatumSystemWas grün warWas gefehlt hatMesswert
3. Juli 2026Bestellimport aus PDFModellaufruf lief durchWechsel von Instruct- auf Thinking-Modell: Bildzweig lieferte nach 65 s null Felder, Textzweig fehlerhaftes JSON65 s, 0 Felder; nach der Korrektur 4,9 s und korrekt
17. August 2026Titelvergabe im ChatAufruf ohne FehlerTitel blieb leer, Abbruchgrund length256 Token Budget: 5 von 5 leer; 2048: 2 von 5 leer; ohne Denken: 0 von 5
28. August 2026Auswertung von BewerbungenHTTP 200 vom GatewayInhalt leer, Abbruchgrund length400 Token Budget: 12 von 12 leer
28. August 2026Prüfung von 36 MCP-Servernalle 36 antworteten auf initialize und tools/listSuchdienst lieferte HTTP 200 mit leerer Trefferliste36 von 36 grün, erst ein echter Lese-Aufruf fand den Ausfall
30. August 2026E-Mail-Archivierungjede Minute „sync finished successfully“ein Postfach archivierte 9,4 Monate lang nichtsFortschrittszeiger 187 bei 46 vorhandenen Nachrichten
7. September 2026Sitzungstitel in unserer Agenten-OberflächeAufruf ohne FehlerTitel blieb die erste Nachricht, Inhalt null64 Token Budget, Abbruchgrund length

Messträger: Die Titel-Messung vom 17. August lief gegen ein Qwen3.5-122B-A10B-Modell im NVFP4-Format hinter unserem Gateway. Die Messung vom 28. August lief über das Gateway auf der thinking-Spur mit echten Bewerbungstexten statt Fülltext; das Modell nennt die Notiz nicht. Eine Nachmessung vom 29. August mit 8192 Token Budget an sechs echten Lebensläufen reproduzierte den Ausfall nicht (6 von 6 vollständig), einer verbrauchte aber 7755 von 8192 Token. Archiv und MCP-Prüfung sind Zustandsmessungen an laufenden Systemen ohne Modell.

Leere Titel bei fünf Läufen je EinstellungBudget 256 Token5 · 5 von 5 leerBudget 2048 Token2 · 2 von 5 leerOhne Denken0 · 0 von 5 leer05
Titelvergabe im Chat, Messung vom 17. August 2026 an unserem Gateway (Qwen3.5-122B-A10B, NVFP4). Für die Einstellung ohne Denken nennt die Notiz kein Budget. (Quelle: Eigene Messung, Zeile 17. August 2026 der Tabelle oben)

Zwei Zeilen verdienen einen genaueren Blick. Beim Archivierungsjob stand der Fortschrittszeiger über der Wirklichkeit: Das Postfach war irgendwann neu aufgebaut worden, die Nummern der Nachrichten begannen wieder bei eins, und alles Neue lag damit unter dem gespeicherten Zeiger. Der Job war erfolgreich im Sinne seiner Definition, er fand nur nichts. Von rund 500 zugestellten Mails konnten wir 79 zurückholen, der Rest war gelöscht, bevor es jemand merkte. Danach bauten wir eine Prüfung ein, die das Alter der neuesten archivierten Nachricht je Quelle misst, mit 14 Tagen als Schwelle.

Bei den Titeln kam derselbe Fehler zweimal in verschiedenen Anwendungen vor. Ein Reasoning-Modell verbraucht sein Token-Budget für das Nachdenken und liefert keinen Inhalt, ohne dass ein Fehler gemeldet wird. Die Betriebsnotiz vom 28. August hält dazu eine Regel fest: bei fehlender Modellantwort keinen leeren Text zurückgeben, sondern eine eigene Ausnahme werfen, einmal mit doppeltem Budget nachfassen und den Grund protokollieren.

Wo sich Verträge still ändern

In allen Fällen blieb die Schnittstelle gleich, und es änderte sich ein Detail des Inhalts: ein anderes Modell, ein anderes Token-Budget, ein anderer Zustand hinter dem Dienst. Auch Felder können umbenannt werden. Die vLLM-Dokumentation schreibt in ihrer aktuellen Fassung, das Feld reasoning_content heiße jetzt reasoning, und ein Client könne sonst still ein leeres reasoning_content lesen, obwohl reasoning gefüllt ist. Die LiteLLM-Dokumentation vereinheitlicht dagegen auf reasoning_content. Welches Feld bei uns an welcher Stelle ankommt, hängt also davon ab, ob ein Gateway dazwischen sitzt. Wir haben das nicht Version für Version nachgemessen.

Logo von vLLM
Die vLLM-Dokumentation nennt das Feld reasoning_content jetzt reasoning. (Quelle: Simple Icons 16.36.0, Datei vllm.svg (Repo CC0, im Eintrag kein Lizenzfeld), Marke: vLLM)

Drei Prüfungen, die jeder Pipeline helfen

Form. Direkt hinter dem Abruf prüfen, ob Typ und Pflichtfelder stimmen. Das ist der Teil, den der n8n-Beitrag beschreibt, und er lässt sich in jedem Werkzeug bauen, auch in einem Python-Skript.

Menge. Zählen, wie viel Ergebnis der Lauf erzeugt hat. Eine Liste mit null Einträgen, ein Text mit null Zeichen oder ein Import mit null Datensätzen ist ein Alarm, solange niemand ausdrücklich festgelegt hat, dass null für diesen Job normal ist.

Alter. Messen, wie alt das jüngste Ergebnis ist. Das war die Prüfung, die das Archiv gefunden hätte, und sie braucht keine Kenntnis darüber, warum etwas ausbleibt.

Eine Skizze für die ersten beiden Prüfungen bei einer Chat-Antwort im üblichen Format:

def pruefe(antwort: dict) -> list[str]:
    fehler = []
    wahl = (antwort.get("choices") or [{}])[0]
    text = (wahl.get("message") or {}).get("content")
    if not text or not text.strip():
        fehler.append("Inhalt leer")
    if wahl.get("finish_reason") == "length":
        fehler.append("Ausgabe abgeschnitten")
    return fehler

Die Skizze ist kein Auszug aus unserem Betrieb. Sie zeigt, wie klein die Prüfung sein kann. Was mit der Fehlerliste passiert, entscheidet der nächste Abschnitt.

Wer den Alarm bekommt

Eine Prüfung, die niemand liest, hilft nicht. Aus unserer Arbeit am Archivierungsjob stammt eine zweite Lehre: Die erste Fassung der neuen Prüfung hing an der Bedingung „bisher wurde nichts gefunden“. Weil ein anderes Postfach seit Wochen dauerhaft auf Fehler stand, wäre die neue Prüfung dauerhaft stumm gewesen. Ein Dauerfehler maskiert dann alle Prüfungen, die nach ihm laufen. Der Trockenlauf hat das gefunden; seitdem hängen beide Bedingungen an einer eigenen Abfrage.

Der Archiv-Check meldet nur bei einem Zustandswechsel, damit nicht alle 15 Minuten dieselbe Mail kommt. Wie wir Dienste mit Uptime Kuma abklopfen und Ausfälle melden lassen, steht im Beitrag zu Uptime Kuma. Wer solche Prüfungen für seine Jobs nicht selbst aufbauen will, findet unser Angebot für den IT-Betrieb. Und wer Sprachmodelle in seine Abläufe einbaut, findet im Überblick KI im Betrieb weitere Fälle, bei denen der Aufbau trägt oder nicht.

Noch zu messen

Offen ist, wie verbreitet die Lücke in unseren eigenen Abläufen heute noch ist. Das haben wir für diesen Beitrag nicht gemessen. Vorgehen: Alle geplanten Jobs je Host auflisten (Cron, Timer, Workflow-Ausführungen), dann je Job prüfen, ob er eine Mengenprüfung hat und ob eine leere Ausgabe einen Alarm auslöst. Ergebnis wäre eine Zahl der Form „n von N Jobs alarmieren bei null Ergebnissen“. Zweite Frage, ebenfalls nur lesend zu beantworten: In den Ausführungshistorien der letzten 30 Tage zählen, wie viele Läufe als erfolgreich markiert sind und null Ausgaben haben.

Was belegt ist und was offen bleibt

Belegt durch die Quellen: die drei Änderungsmuster und das Rezept aus dem n8n-Beitrag, die Beschreibung des Consumer-Driven-Ansatzes bei Pact, die Umbenennung von reasoning_content in reasoning bei vLLM, die Vereinheitlichung bei LiteLLM. Belegt durch unsere Betriebsnotizen: die sechs Fälle in der Tabelle mit den dort genannten Zahlen.

Offen: wie oft das in unseren Abläufen heute vorkommt (siehe oben), ob der Feldname hinter unserem Gateway je nach Version abweicht, und ob die Prüfung des Alters in jedem Job eine sinnvolle Schwelle hat. Quellen zuletzt geprüft: 5. November 2026. Die n8n-Angaben stammen vom Hersteller.

Zehn Minuten am Montag

  1. Suchen Sie den Job heraus, dessen Ergebnis in eine Mail, eine Buchung oder eine Entscheidung fließt.
  2. Öffnen Sie seine letzten 30 Läufe und zählen Sie, wie viele Ausgaben jeder Lauf erzeugt hat. Steht dort bei einem als erfolgreich markierten Lauf eine Null, haben Sie den Fehler gefunden.
  3. Prüfen Sie, ob ein Dauerfehler an anderer Stelle Ihre Fehlermeldung stummschaltet.
  4. Legen Sie eine Regel fest: null Ausgaben sind ein Alarm, und er geht an eine Person, die ihn auch liest.

Weiterführende Quellen

Fragen?
Was ist ein stiller Fehler in einer Automation?+

Ein Lauf, der technisch gelingt und fachlich nichts liefert. Der Statuscode ist 200, der Job meldet Erfolg, aber das Feld ist leer, die Liste hat null Einträge oder ein Wert hat einen anderen Typ als erwartet. Fehlerbehandlung greift hier nicht, weil formal nichts schiefgegangen ist.

Reicht ein Schema-Check, um solche Fehler zu finden?+

Er findet geänderte Felder und Typen, etwa eine Zahl, die plötzlich als Text ankommt. Eine leere, aber formal gültige Antwort findet er nur, wenn das Schema Mindestlängen oder Mindestanzahlen vorschreibt. Deshalb gehört eine zweite Prüfung dazu, die zählt, wie viel Ergebnis ein Lauf tatsächlich erzeugt hat.

Warum liefert ein Reasoning-Modell manchmal eine leere Antwort?+

Weil Nachdenken und Antwort sich ein Token-Budget teilen können. Ist es aufgebraucht, bevor das erste Zeichen der Antwort kommt, gibt die Schnittstelle einen leeren Inhalt mit dem Abbruchgrund length zurück. Bei uns war das in den Messungen vom 17. und 28. August 2026 der Fall, ohne Fehlermeldung.