‘Unexpected token <’, ‘is not valid JSON’ of ‘Unexpected end of JSON input’? Je krijgt HTML of niets in plaats van JSON. Zo vind en los je het op.
Je JavaScript verwacht JSON (gegevens in tekstvorm, zoals {"naam":"Jan"}) en probeert die te lezen met JSON.parse() of response.json(). Wat er binnenkwam was geen geldige JSON. Het teken waar het misgaat, staat in de melding. Een < op positie 0 betekent bijna altijd: de server stuurde een HTML-pagina terug in plaats van JSON. Recente versies van Chrome en Node.js tonen daarbij het begin van die tekst, zoals "<!DOCTYPE "....
Waarom stuurt een server HTML? Omdat er iets anders misging en de server een foutpagina terugstuurde: een 404 omdat de URL niet klopt, een 500 omdat het script op de server crashte, een inlogpagina omdat je sessie verlopen is, of een HTML-pagina van een firewall of onderhoudsmelding. Bij PHP komt het ook vaak voor dat een waarschuwing (Warning, Notice, Deprecated) vóór de JSON wordt afgedrukt; die begint meestal met <br />, dus ook met een <.
Unexpected end of JSON input betekent dat het antwoord leeg was of halverwege ophield. Denk aan een server die bij een fout niets terugstuurt, een antwoord met status 204 (No Content), een script dat stopt voordat het iets afdrukt, of een JSON.parse('') op een lege waarde uit localStorage.
SyntaxError, meestal bij een regel met response.json() of JSON.parse.<!DOCTYPE html> of <br /> begint.Menunamen kunnen per versie of toestel iets anders heten. Loop de stappen op volgorde af en stop zodra het werkt.
Open F12, ga naar Network, herlaad of herhaal de actie en klik op het verzoek. Kijk naar de statuscode, de header Content-Type en het tabblad Response. Daar zie je meteen wat er wél terugkwam: een foutpagina, een inlogscherm, een PHP-waarschuwing of niets. Dat antwoord vertelt je wat de echte fout is.
Een verkeerde of relatieve URL is een veelvoorkomende oorzaak. fetch('api/producten') zonder / ervoor wordt relatief aan de huidige pagina opgevraagd en kan op een andere plek uitkomen. Op veel sites stuurt de server een onbekende URL door naar de homepage of een 404-pagina in HTML. Gebruik het volledige pad en open het adres eens rechtstreeks in de browser.
// relatief, kan op een andere plek uitkomen:
fetch('api/producten')
// absoluut pad:
fetch('/api/producten')Lees de gegevens pas als JSON als het antwoord goed is en ook echt JSON is. Zo krijg je een duidelijke foutmelding met de echte status, in plaats van een vage SyntaxError. Lees bij twijfel eerst als tekst, zodat je in de console ziet wat er binnenkwam.
const res = await fetch('/api/producten');
const type = res.headers.get('content-type') || '';
if (!res.ok || !type.includes('application/json')) {
const tekst = await res.text();
throw new Error('Status ' + res.status + ': ' + tekst.slice(0, 200));
}
const data = await res.json();Bij Unexpected end of JSON input was het antwoord leeg. Lees dan geen JSON bij status 204, en controleer bij JSON.parse eerst of er iets is om te lezen. Vang de fout af met try/catch waar een lege of kapotte waarde kan voorkomen, zoals bij localStorage.
if (res.status === 204) return null;
let instellingen = {};
try {
instellingen = JSON.parse(localStorage.getItem('instellingen') || '{}');
} catch (e) {
instellingen = {};
}Zet de juiste header en zorg dat er niets anders wordt afgedrukt: geen spaties of lege regels vóór <?php, geen echo voor debuggen en geen waarschuwingen. Zet display_errors uit op een productieserver en schrijf fouten naar het log. Een witregel of waarschuwing vóór de header geeft ook headers already sent.
<?php
ini_set('display_errors', '0');
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => true, 'items' => $items]);
exit;Laat je API bij een fout een JSON-antwoord met een passende statuscode sturen, in plaats van een HTML-foutpagina. Dan kan je JavaScript de fout netjes tonen. Controleer in het foutenlog van de server waarom het verzoek mislukte; bij een 500 staat daar de echte oorzaak.
<?php
http_response_code(400);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['ok' => false, 'fout' => 'Ongeldige invoer']);
exit;Krijg je een HTML-inlogpagina terug, dan is je sessie verlopen of worden de cookies niet meegestuurd; bij een ander domein heb je credentials: 'include' nodig. Een firewall, Cloudflare of een onderhoudsmodus kan ook een HTML-pagina teruggeven, bijvoorbeeld een uitdaging of een 429 Too Many Requests. Het tabblad Response laat zien welke het is.
Let op: Zet display_errors niet aan op een live server om de fout te zien. Behalve dat het je JSON breekt, toont het bestandspaden en soms gegevens aan iedere bezoeker. Lees de fout in het serverlog.
Kom je er met deze stappen niet uit, of wil je het liever niet zelf doen? We kijken online mee of komen langs. Hulp op afstand kost €20 per uur; komen we langs, dan is de prijs op aanvraag. Je weet vooraf waar je aan toe bent.
Schrijf kort wat er misgaat en wat je al hebt geprobeerd. Kies of we bellen, online meekijken of langskomen.
Het eerste teken van het antwoord is een <, dus de server stuurde HTML in plaats van JSON. Meestal is dat een foutpagina, een inlogpagina of een PHP-waarschuwing.
Het antwoord was leeg of hield halverwege op. Kijk in het tabblad Network of er wel iets terugkwam en welke statuscode erbij hoort.
Open F12, ga naar Network, klik op het verzoek en kijk bij Response. Of lees het antwoord in je code eerst met res.text() en log het.
Dan begint het antwoord met gewone tekst, bijvoorbeeld een PHP-waarschuwing zonder HTML-opmaak of een foutmelding als platte tekst. Het principe is hetzelfde: er staat iets anders dan JSON.
Hulp nodig van een mens? Website-onderhoud · Hosting en onderhoud · WordPress-support · Alle oplossingen