SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON —— bedeutet fast immer eines: dein Code hat JSON.parse() (oder res.json()) auf eine HTML-Seite, kein JSON angewendet. Das < an Position 0 ist der Anfang von <!DOCTYPE html> oder <html>. Hier ist genau, warum das passiert und wie du es behebst.
Wie der Fehler aussieht
// V8 (Chrome / Node / Edge)
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
// Älteres V8
SyntaxError: Unexpected token < in JSON at position 0
// Firefox
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
// Safari
SyntaxError: JSON Parse error: Unexpected identifier "<"Position 0 ist der entscheidende Hinweis: das allererste Byte ist schon falsch, also war die Antwort von Anfang an nie JSON.
Warum es passiert: du hast HTML bekommen
Ein fetch- oder AJAX-Aufruf hat ein HTML-Dokument anstelle des erwarteten JSON zurückgegeben. Übliche Quellen:
- Eine 404 / 500 Fehlerseite, die dein Framework oder Web-Server als HTML ausliefert
- Eine Login- oder Auth-Weiterleitung auf eine HTML-Seite (Session abgelaufen)
- Eine falsche URL —— du triffst die App/SPA-Route statt des API-Endpoints
- Ein Proxy, CDN oder Captive Portal, der seine eigene HTML-Seite zurückgibt
Kaputtes Beispiel
const res = await fetch('/api/user'); // Server hat eine 404-HTML-Seite zurückgegeben
const data = await res.json(); // ❌ Unexpected token '<' …
// res.json() versucht "<!DOCTYPE html>…" als JSON zu parsenBehobenes Beispiel
const res = await fetch('/api/user');
// 1) Status vor dem Parsen prüfen
if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}
// 2) Optional: bestätigen, dass es wirklich JSON ist
const type = res.headers.get('content-type') ?? '';
if (!type.includes('application/json')) {
const text = await res.text();
throw new Error(`Expected JSON, got: ${text.slice(0, 80)}…`);
}
const data = await res.json(); // ✅ sicherZwei weniger offensichtliche Quellen, die man kennen sollte
- WAF- oder Rate-Limit-geblockte Antworten. Eine WAF, ein CDN oder eine DDoS-Schutzschicht kann den Aufruf abfangen und ihre eigene HTML-Challenge- / Block-Seite zurückgeben —— Cloudflare-Interstitial, AWS-WAF-Ablehnungsseite, die Captive-Portal-Wand öffentlicher WLANs. Der Status ist oft
403/429/503mit HTML-Body. Untersuche die Antwort im Network-Tab und schau, ob Header wieCF-RayoderX-Amz-Cf-Iddabei sind, um das zu erkennen. - Der SPA-History-Fallback, der
index.htmlzurückgibt. Static-Hosts und Dev-Server schreiben jeden unbekannten Pfad oft auf/index.htmlum, damit der Client-Router übernimmt. Wenn dein Frontend versehentlich/api/userals relativen Pfad fetcht und der Proxy nicht konfiguriert ist, gibt der Server stillschweigend die HTML-Hülle deiner SPA mit200zurück —— und du bekommst eine verwirrende „200 OK aber HTML"-Version desselben Fehlers.
So behebst du es —— Schritt für Schritt
- Logge den rohen Body:
console.log(await res.clone().text()). Wenn er mit<!DOCTYPEoder<html>beginnt, ist es HTML. - Prüfe
res.ok/res.statusbevor dures.json()aufrufst —— ein 4xx/5xx liefert typischerweise eine HTML-Fehlerseite. - Bestätige die URL im Network-Tab. Ein relativer Pfad kann sich auf die index.html deiner SPA statt auf die API auflösen.
- Prüfe die Authentifizierung —— eine Weiterleitung zur Login-Seite bedeutet, dass dein Token/deine Session abgelaufen ist.
- Inspiziere
Content-Type—— verzweige danach, um Nicht-JSON-Antworten zu behandeln, statt sie blind zu parsen.
Häufig gestellte Fragen
Was bedeutet „Unexpected token < in JSON at position 0"?
Der Parser hat < als erstes Zeichen gefunden, was niemals gültiges JSON ist. Die Antwort war HTML (eine <!DOCTYPE>- oder <html>-Seite), nicht das JSON, das dein Code erwartet hat.
Warum gibt mein fetch HTML statt JSON zurück?
Häufige Ursachen: 404/500-Fehlerseite, Auth-Weiterleitung auf eine Login-Seite, oder falsche URL, die zu deinem Frontend statt zur API auflöst. Prüfe res.ok und logge await res.text(), um zu sehen, was zurückkam.
Wie verhindere ich, dass es meine App kaputt macht?
Guard vor dem Parsen: prüfe res.ok und den Content-Type-Header, und wickle JSON.parse()/res.json() in try/catch, sodass eine HTML-Antwort zu einem behandelten Fehler statt zu einer Exception wird.
Jetzt beheben
Wenn du einen Response-Body hast, bei dem du unsicher bist, füge ihn in JSON Fix ein —— es sagt dir sofort, ob es gültiges JSON oder etwas anderes (wie HTML) ist. Alles läuft in deinem Browser.
- JSON Fix —— JSON im Browser validieren und reparieren
- Wie man JSON.parse-„Unexpected Token"-Fehler behebt —— jede Token-Variante
- Unexpected token u in JSON at position 0 —— der
undefined-Zwillingsfehler - „[object Object] is not valid JSON" beheben —— die vollständige Syntax-Fehler-Referenz