Warum scheitert von KI erzeugtes JSON an JSON.parse()? Ursachen und Lösungen

Stand 17. September 2026: Eine Chat-Antwort direkt an JSON.parse() zu geben scheitert meist an Fences, Fließtext, trailing commas, Abbruch und JS-Dialekt — nicht daran, dass das Modell kein JSON kann. Ursachenkarte und Fix-Reihenfolge.

Vorab: Ein JSON.parse()-Fehler heißt meist nicht „das Modell kann kein JSON schreiben“. Er heißt: Sie haben eine ganze Chat-Antwort einem Parser gegeben, der genau einen JSON-Wert akzeptiert.JSON.parse akzeptiert einen einzelnen Wert der JSON-Grammatik (ECMA-262 / RFC 8259). Markdown-Zäune, umschließende Prosa, Trailing Commas, nackte Zeilenumbrüche, Truncation und JS-/Python-Dialekt werfen sofort SyntaxError. Die Reparaturreihenfolge 2026: Wenn Structured Output oder das Tool-Calling-Feld arguments verfügbar ist, parsen Sie keine Chat-Prosa; wenn Sie parsen müssen, zuerst extrahieren, dann parse, dann mit JSON Schema validieren — nicht zuerst mit Regex „zurechtflicken, bis es irgendwie parst“.

Stand 17. September 2026. Diese Seite hat bereits Was Structured Output ist, Vom Prompt zu Structured Output, OpenAI vs Gemini Structured Output, strukturiertes JSON mit der Gemini API und Tool Calling und JSON Schema. Dieser Text beantwortet nur, warum Modellausgabe an JSON.parse scheitert — und in welcher Reihenfolge Sie das beheben.

Was JSON.parse tatsächlich akzeptiert

In Browsern und Node implementiert JSON.parse JSON-Text, nicht „ein JavaScript-Objektliteral, das nah genug aussieht“. Whitespace (Leerzeichen, Tab, Line Feed, Carriage Return) darf den Wert umgeben. Ansonsten muss die Eingabe genau ein Wert sein: Objekt, Array, String, Zahl, true / false / null. Nicht-Whitespace danach scheitert — Chrome schreibt oft Unexpected non-whitespace character after JSON.

Diese Formen laufen in JS und sterben in JSON. Modelle kopieren sie ständig aus Trainingsdaten:

FormJS-Objekt / JSON5JSON.parse
Trailing Comma{"ok": true,} okwirft
Einfache Anführungszeichen{'ok': true} okwirft
Kommentare// note okwirft
Nackte Keys{ok: true} okwirft
undefined / NaN / Infinityexistieren in der Sprachewirft
Nackter Zeilenumbruch in einem StringTemplate-Strings erlauben eswirft; muss \n sein

Beim Debuggen eine Frage: Haben Sie „einen JSON-Wert“ übergeben oder „einen Absatz, den das Modell lesbarer verpackt hat“? Der Parser ist für das Erste zuständig. Das Zweite müssen Sie extrahieren.

Eine Tabelle der Fehlerklassen

Klassifizieren Sie den SyntaxError, bevor Sie mit der genauen Formulierung ringen. Chrome, Safari und Node formulieren denselben Bug unterschiedlich. Die Klassen sind wenige:

KlasseWas das Modell oft emittiertTypisches ErgebnisDas zuerst tun
Hülle```json-Zäune, „hier ist das JSON“Erstes Zeichen ist nicht { / [Zaun abziehen, dann einen balancierten Wert schneiden
DialektTrailing Commas, einfache Anführungszeichen, Kommentare, nackte KeysUnexpected tokenAuf Structured Output wechseln; nicht als JS parsen
Beschädigter StringUnescaped ", nackte Zeilenumbrüche, Vollbreite-KommasString endet zu früh, oder kein : nach einem KeyDie Spalte lesen; Feldlänge deckeln
TruncationUnvollständiges Objekt oder ArrayUnexpected end of JSON inputAusgabe-Limit erhöhen; auf den Stream warten
Mehrere WerteZwei JSON-Werte, oder Prosa nach dem erstenZeichen nach dem ersten WertNur den ersten vollständigen Wert schneiden
KodierungBOM, Zero-Width-Zeichen, doppeltes stringifySeltsames Token, oder parse liefert einen StringBOM entfernen; typeof prüfen, bevor Sie erneut parsen

Für Agenten noch eine: Tool-Calling-arguments sind oft schon ein Objekt oder ein JSON-String, den der Vendor bereits eingeschränkt hat. Schicken Sie nicht die ganze Assistant-Nachricht durch JSON.parse. Das ist ein anderer Kanal — siehe warum Tool Calling von JSON Schema abhängt.

Zäune und umschließende Prosa

Chat-Modelle sind trainiert, Code in Zäune zu legen. Selbst wenn Sie „nur JSON“ geschrieben haben, sieht die Antwort oft so aus:

```json
{"ok": true, "id": "A-1024"}
```
Here is the result. I can explain the fields if you want.

Das erste Zeichen ist ein Backtick, nicht {. JSON.parse scheitert in Spalte 0. Ein führendes „Klar, hier ist das JSON:“ oder ein Disclaimer am Ende ist derselbe Bug. Schlimmer: zwei Werte — ein Sample, dann das echte Ergebnis. Parsen Sie den ganzen Blob, und Sie sterben nach der ersten }.

Extrahieren Sie mit einer Regel: Finden Sie das erste balancierte {} oder [] (Klammern in Strings überspringen) und übergeben Sie nur diesen Ausschnitt an JSON.parse. Zäune zuerst abziehen. Nicht gierig vom ersten { bis zum letzten } schneiden — Klammern in Strings oder ein zweites Objekt in der Erklärung schneiden falsch.

Dialekt: Trailing Commas, einfache Anführungszeichen, Kommentare, nackte Keys

Modelle haben Massen von JavaScript, Python, JSON5 und YAML gesehen. Bei „strukturierten Daten“ mischen sie Dialekte. Alles Folgende ist für JSON.parse illegal:

{
  ok: true,          // bare key + comment
  'name': 'Ada',     // single quotes
  "tags": ["a",],    // trailing comma
  "flag": True       // Python boolean
}

Dazu kommen undefined, NaN, Infinity, None. In ihren Sprachen bedeuten sie etwas; JSON hat null und endliche Zahlen. JSON.parse durch eval oder new Function zu ersetzen, um diese Formen „zu akzeptieren“, macht den Parser zur Senke für beliebigen Code. In Produktion nicht tun.

JSON5 und JSONC schlucken Kommentare und Trailing Commas. Das ist in Ordnung für Menschen, die Config editieren. Als Default-Parser für Modellausgabe taugt das schlecht. Sobald Sie die Grammatik lockern, können Sie „Komma zu viel“ nicht mehr von „beschädigtem String“ unterscheiden. Brauchen Sie eine lockere Schicht, halten Sie sie hinter Extract + Parse-Fehler, und fahren Sie nach einer Reparatur trotzdem Schema.

Strings und Interpunktion: Escapes, Zeilenumbrüche, Vollbreite und typografische Anführungszeichen

Ein legaler JSON-String nutzt doppelte Anführungszeichen. Innere " und Backslashes müssen escaped werden. Steuerzeichen müssen \n, \t oder \uXXXX sein. Kopiert ein Modell einen Nutzerkommentar, landen nackte Anführungszeichen und Zeilenumbrüche im Feld. Der String endet zu früh; das nächste Komma oder CJK-Zeichen wird zum unexpected Token.

CJK-Ausgabe bringt einen häufigen Schmutzsatz: Vollbreite-Komma ,, Vollbreite-Doppelpunkt : und typografische Anführungszeichen “” / ‘’. Sie sehen aus wie Interpunktion; ihre Codepoints sind nicht 0x2C / 0x3A / 0x22. Dieses „fast JSON“ stirbt nach dem name-Wert:

{
  "name": "Ada",
  "ok": true
}

Beheben Sie das nicht mit einem weiteren Satz „bitte ASCII-Interpunktion“. Setzen Sie maxLength auf lange String-Felder, lassen Sie das Modell Quelltext zitieren statt Interpunktion abzutippen, und nutzen Sie Structured Output auf dem finalen Kanal. Zum Debuggen in den JSON-Validator einfügen und sehen, in welcher Spalte das Highlight stoppt — ein Vollbreite-Komma ist offensichtlich.

Truncation und Streaming: Unexpected end of JSON input

Unexpected end of JSON input heißt fast immer: Der Text endete, bevor die Grammatik endete — fehlende }, fehlende ] oder ein nicht geschlossener String. 2026 sind die üblichen Quellen ein Ausgabe-Token-Limit, ein Safety-Cut oder Sie haben JSON.parse auf einem unvollständigen Stream-Chunk aufgerufen.

Eine Streaming-API liefert Deltas. Frühe Chunks können {"ok": tr sein. Das zu parsen scheitert. Stattdessen:

  • Warten, bis der Stream endet (finish_reason / stop), dann den vollen Puffer parsen;
  • Oder einen echten Streaming-JSON-Parser nutzen, der Token für Token fortschreitet — nicht JSON.parse auf einem halben Wert aufrufen;
  • Ist der Stop-Grund length / max_tokens, ist das kein Parse-Bug. Die Generierung ist nicht fertig — Limit erhöhen, Schema verkleinern oder das Modell paginieren.

Nach einem Abschneiden automatisch Klammern zu schließen ist ein Draft-Trick. Die Form kann parsen und trotzdem Felder fehlen oder einen String halbieren. Nach jeder Reparatur Schema-validieren; bei Fehler retry. Nicht stillschweigend speichern.

Unsichtbare Zeichen und Doppelkodierung

Ein UTF-8-BOM (U+FEFF) ist kein JSON-Whitespace. Manche Copy-Pfade und Gateways setzen ihn davor; JSON.parse meldet dann ein unexpected Token in Spalte 0. Zero-Width-Spaces und Soft Hyphens tun dasselbe. Vor dem Extrahieren mit replace(/^\uFEFF/, "") entfernen, dann trim.

Doppelkodierung ist leiser. Ein JSON.stringify liefert den String "{\"ok\":true}". Parsen Sie diese gequotete Form, bekommen Sie den String {"ok":true}, kein Objekt. Ein zweites Parse liefert das Objekt. Stoppen Sie nach einem Parse und lesen Sie .ok, bekommen Sie undefined — es hat „geparst“ und hat keine Felder. Prüfen Sie typeof, bevor Sie erneut parsen. Nicht fest verdrahten „immer zweimal parsen“; ein echtes Objekt wirft.

Reparaturreihenfolge: Kanal wechseln, dann extrahieren, zuletzt reparieren

Diese Reihenfolge schlägt das Stapeln weiterer Prompt-Sätze:

  1. Kanal wechseln. Finale Antworten gehen über Structured Output (OpenAI response_format.json_schema, Gemini responseMimeType plus Schema, Claude output_config.format). Tool-Parameter gehen über Tool-Calling-arguments, nicht über abgeschabte Prosa. Siehe was Structured Output ist.
  2. Extrahieren. ```json-Zäune abziehen; den ersten balancierten Wert schneiden; ein BOM entfernen.
  3. Strikt parsen. Nur JSON.parse. Bei Fehler den Rohtext und die Fehlerposition behalten. Nicht eval.
  4. Das Schema validieren. Ein erfolgreiches Parse heißt nur: die Grammatik ist legal. Fehlende Felder, falsche Typen und Extra-Keys brauchen JSON Schema / ajv. Siehe vom Prompt zu Structured Output.
  5. Zuletzt reparieren. Werkzeuge wie jsonrepair können Klammern schließen und Trailing Commas entfernen. Nur nutzen, nachdem Extract + Parse gescheitert sind, und nur wenn Sie akzeptieren, dass Reparaturen die Bedeutung ändern können. Dann trotzdem Schritte 3 und 4. Keinen Repairer zum globalen Default-Parser machen.

Prompts helfen trotzdem: „keine Zäune, keine Erklärung“. Sie senken die Chance auf eine Hülle. Sie ersetzen kein Schema und lockern JSON.parse nicht. 2026 Chat-Prosa als API zu behandeln heißt: Sie zahlen weiter für Zäune und Truncation.

Eine kleine Extract-+-Parse-Pipeline

Eine Pipeline in Lehrgröße: Zäune abziehen, BOM entfernen, einen balancierten Wert schneiden, dann JSON.parse. Sie behandelt übliche Hüllen. Sie repariert keine Trailing Commas und keine Vollbreite-Interpunktion — das bleibt Structured Output oder einer expliziten Reparaturschicht.

function stripFence(text) {
  const m = String(text).match(/```(?:json|JSON)?\s*([\s\S]*?)```/);
  return m ? m[1] : String(text);
}

function sliceBalancedJson(text) {
  const src = text.replace(/^\uFEFF/, "").trim();
  const start = src.search(/[\{\[]/);
  if (start < 0) throw new SyntaxError("No JSON value found");
  const open = src[start];
  const close = open === "{" ? "}" : "]";
  let depth = 0, inStr = false, esc = false;
  for (let i = start; i < src.length; i++) {
    const ch = src[i];
    if (inStr) {
      if (esc) { esc = false; continue; }
      if (ch === "\\") { esc = true; continue; }
      if (ch === '"') inStr = false;
      continue;
    }
    if (ch === '"') { inStr = true; continue; }
    if (ch === open) depth++;
    else if (ch === close) {
      depth--;
      if (depth === 0) return src.slice(start, i + 1);
    }
  }
  throw new SyntaxError("Unterminated JSON value");
}

function parseModelJson(raw) {
  return JSON.parse(sliceBalancedJson(stripFence(raw)));
}

Der Slicer muss tracken, ob er in einem String ist, sonst schließt ein { in einem Feldwert zu früh. Verschachtelte Objekte und Arrays nutzen depth. Scheitert der Ausschnitt trotzdem am Parse, den fehlgeschlagenen Text in den Validator einfügen und die Klassentabelle oben nutzen. Auf dieser Schicht kein weiteres Regex stapeln.

Den Fehler lokal sehen

Schicken Sie Modellausgabe nicht direkt in einen Produktionsparser. Im Browser drei Dinge prüfen: Ist es legales JSON; wenn nicht, welche Spalte; wenn Sie schon ein Schema haben, erfüllt es den Vertrag.

  • JSON-Validator — sehen, wo SyntaxError landet; ein Schema anhängen, wenn Sie eines haben.
  • JSON-Formatierer — wenn es formatiert, parst es meist; wenn es scheitert, im Quelltext nach Vollbreite-Kommas oder Zäunen suchen.
  • JSON Diff — nach erfolgreichem Parse das Modellobjekt mit dem Minimalobjekt vergleichen, das Sie erlauben.

Nichts verlässt den Browser. Das passt zu einer fehlgeschlagenen Modellantwort, einem Schema und einem Tool-Calling-arguments-Blob nebeneinander. Feldnamen und required stabilisieren, dann den Host verdrahten.

FAQ

Warum scheitert „sieht aus wie JSON“ trotzdem an JSON.parse?

Das Auge toleriert Zäune, Trailing Commas, typografische Anführungszeichen und umschließende Prosa. JSON.parse akzeptiert genau einen RFC-8259-Wert. Wie JSON auszusehen ist nicht dasselbe wie legales JSON.

Reicht ein Regex, das ```json-Zäune entfernt?

Nein. Zäune sind nur eine Hülle. Danach kommen Prosa, ein zweiter JSON-Wert, Trailing Commas und Truncation. Nach dem Abziehen der Zäune einen balancierten Wert schneiden und strikt parsen.

Worin unterscheidet sich JSON Mode von Structured Output?

JSON Mode beschränkt meist nur „sieht aus wie JSON“, nicht Felder und Typen. Structured Output blockiert mit JSON Schema illegale Token zur Decode-Zeit. Soll ein Programm das Ergebnis verbrauchen, Structured Output bevorzugen. Nicht JSON Mode einschalten und dann den Chat-Body mit JSON.parse parsen.

Sollten jsonrepair oder JSON5 der Default-Parser sein?

Nein. Sie akzeptieren Input, der scheitern sollte, und können die Bedeutung ändern. Nur als Reparaturschicht nach fehlgeschlagenem Extract + JSON.parse, danach trotzdem Schema-validieren.

Wann darf ich JSON.parse auf einer gestreamten Antwort aufrufen?

Nachdem der Stream endet und der Puffer ein vollständiger Wert ist. Ein halber Chunk liefert jedes Mal Unexpected end of JSON input. Token beim Eintreffen verbrauchen: Streaming-Parser, nicht JSON.parse.

Parse hat geklappt, aber die Felder sind falsch. Gehört das in diesen Artikel?

Das ist die nächste Schicht. JSON.parse garantiert nur Grammatik. Fehlende Felder, falsche Typen und Extra-Keys sind Schema-Probleme — siehe Structured Output und Tool-Calling-Validierung auf dieser Seite.

Fazit

Ein JSON.parse-Fehler ist ein Kanalproblem. Ein weiterer Satz „bitte gib JSON aus“ behebt das nicht. Chat-Modelle legen Zäune drum, mischen Dialekte und stoppen am Token-Limit. Der Parser akzeptiert einen sauberen JSON-Wert. 2026 das Modell über Structured Output oder Tool-Calling-arguments verdrahten; dann Extract + striktes Parse + Schema; zuletzt reparieren.

Prompts können Hüllen reduzieren. Die Grammatik lockern sie nicht. Den fehlgeschlagenen Text in einen lokalen Validator einfügen, sehen, in welcher Spalte er stoppt, dann entscheiden: Zaun abziehen, Kanal wechseln oder das Ausgabe-Limit erhöhen. Modelle wechseln. Was JSON.parse akzeptiert und was Ihr Feldvertrag ist, sollte es nicht.