Python JSONDecodeError: „Expecting value: line 1 column 1 (char 0)“ und andere Meldungen beheben

„Expecting value: line 1 column 1 (char 0)“ heißt: Python hat an der allerersten Stelle etwas erwartet, das mit JSON beginnt, und stattdessen etwas anderes gefunden. Fast immer ist der Text leer, enthält eine HTML-Fehlerseite statt JSON, oder du hast ihn schon einmal gelesen. Die Ursache liegt also meist nicht im Parser, sondern in dem, was du ihm übergibst.

Wenn du das JSON vor dir hast, füge es in den JSON-Formatierer ein. Er zeigt Zeile und Spalte der Fehlerstelle und erklärt sie auf Deutsch. Die Verarbeitung läuft komplett lokal im Browser, es wird nichts an einen Server gesendet.

Was ein JSONDecodeError enthält

json.JSONDecodeError ist eine Unterklasse von ValueError. Die Ausnahme trägt die Fehlerstelle als Attribute:

Attribut Bedeutung
msg Meldung ohne Positionsangabe
doc der Text, der geparst wurde
pos Zeichenposition ab Anfang, beginnend bei 0
lineno Zeile, beginnend bei 1
colno Spalte, beginnend bei 1

str(e) setzt daraus die bekannte Meldung zusammen: msg: line <lineno> column <colno> (char <pos>). Beachte die unterschiedliche Zählweise: char 0 ist das erste Zeichen, column 1 ebenfalls.

Die häufigsten Meldungen

Die folgenden Meldungen stammen aus Python 3.9 bis 3.12. Ab Python 3.13 meldet Python ein Komma vor } oder ] mit eigenem Text: Illegal trailing comma before end of object bzw. Illegal trailing comma before end of array. Die übrigen Meldungen sind unverändert.

Meldung Typische Ursache
Expecting value: line 1 column 1 (char 0) leerer Text, HTML statt JSON, None/True als Text
Expecting value an späterer Stelle fehlender Wert nach :; bis Python 3.12 auch Komma vor ]
Expecting property name enclosed in double quotes Single Quotes, Schlüssel ohne Anführungszeichen; bis Python 3.12 auch Komma vor }
Illegal trailing comma before end of object / … of array Komma vor } bzw. ] (ab Python 3.13)
Expecting ',' delimiter Komma zwischen zwei Einträgen vergessen
Expecting ':' delimiter Doppelpunkt zwischen Schlüssel und Wert fehlt
Extra data nach dem ersten gültigen Wert steht noch etwas
Unterminated string starting at schließendes " fehlt
Invalid control character at rohes Zeilenumbruch- oder Tabulatorzeichen in einem String

Hier die Fälle zum Nachbauen, jeweils mit der Meldung, die Python ausgibt:

import json

json.loads('{"a": 1,}')
# bis 3.12: Expecting property name enclosed in double quotes: line 1 column 9 (char 8)
# ab 3.13:  Illegal trailing comma before end of object: line 1 column 8 (char 7)

json.loads('{"a": 1 "b": 2}')
# Expecting ',' delimiter: line 1 column 9 (char 8)

json.loads('{"a": 1}{"b": 2}')
# Extra data: line 1 column 9 (char 8)

json.loads('{"a": "abc')
# Unterminated string starting at: line 1 column 7 (char 6)

json.loads('{"a": "x\ny"}')   # \n ist hier ein echter Zeilenumbruch im String
# Invalid control character at: line 1 column 9 (char 8)

json.loads('[1, 2,]')
# bis 3.12: Expecting value: line 1 column 7 (char 6)
# ab 3.13:  Illegal trailing comma before end of array: line 1 column 6 (char 5)

Der Parser meldet die Stelle, an der er nicht weiterkommt, und die liegt oft hinter dem eigentlichen Fehler. Beim Komma vor } zeigt Python bis Version 3.12 auf die Stelle, an der der nächste Schlüssel stehen müsste; ab 3.13 zeigt es direkt auf das überzählige Komma. Mehr dazu im Ratgeber JSON-Fehler finden und beheben.

Ursache 1: leere Antwort oder HTML statt JSON

Der Klassiker bei requests: Der Server antwortet mit einem Fehlerstatus, einer Weiterleitung oder einer Login-Seite. Der Body ist leer oder beginnt mit <, und response.json() scheitert bei char 0.

import requests

response = requests.get("https://example.com/api/users")
data = response.json()   # JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Prüfe vor dem Parsen Status und Inhalt:

response = requests.get("https://example.com/api/users", timeout=10)
print(response.status_code)
print(response.headers.get("Content-Type"))
print(response.text[:200])   # die ersten Zeichen verraten sofort, was ankommt

Beginnt der Text mit <!DOCTYPE html> oder <html>, ist es eine HTML-Seite, zum Beispiel ein 404, 502 oder eine Anmeldung. Ist er leer, liefert der Server nichts, etwa bei Status 204. Robuster:

response = requests.get("https://example.com/api/users", timeout=10)
response.raise_for_status()          # wirft bei 4xx/5xx eine eigene Ausnahme
if not response.text.strip():
    raise ValueError("Leere Antwort vom Server")
data = response.json()

Neuere requests-Versionen (ab 2.27) lösen bei response.json() eine requests.exceptions.JSONDecodeError aus. Sie erbt von ValueError und – solange simplejson nicht installiert ist – auch von json.JSONDecodeError, sodass beide except-Varianten greifen.

Ursache 2: Single Quotes durch str(dict)

Wer ein Python-Dict mit str() oder print() in eine Datei schreibt, erzeugt keine JSON-Datei:

user = {"name": "Ada", "aktiv": True, "alter": None}
print(str(user))
# {'name': 'Ada', 'aktiv': True, 'alter': None}

Das ist Python-Syntax: einfache Anführungszeichen, True und None. JSON verlangt doppelte Anführungszeichen, true und null. json.loads meldet hier Expecting property name enclosed in double quotes: line 1 column 2 (char 1). Erzeuge JSON immer mit json.dumps:

import json

text = json.dumps(user, ensure_ascii=False)
# {"name": "Ada", "aktiv": true, "alter": null}

Hast du nur den kaputten Text und kein Original, hilft ast.literal_eval(text) bei reinen Python-Literalen. Das ist ein Notbehelf; behebe besser die Stelle, die den Text erzeugt. Zu Anführungszeichen im Detail: Anführungszeichen in JSON.

Ursache 3: BOM am Dateianfang

Manche Windows-Programme speichern UTF-8 mit Byte Order Mark. Öffnest du die Datei mit encoding="utf-8", bleibt das unsichtbare Zeichen \ufeff am Anfang stehen. Python meldet dann ausdrücklich:

Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)

Die Lösung steht schon in der Meldung:

import json

with open("daten.json", encoding="utf-8-sig") as f:
    data = json.load(f)

utf-8-sig entfernt ein BOM, falls vorhanden, und liest Dateien ohne BOM ganz normal. Hintergründe stehen im Ratgeber BOM in JSON-Dateien.

Ursache 4: Datei zweimal gelesen

Ein Dateiobjekt merkt sich seine Leseposition. Nach json.load(f) steht sie am Ende. Ein zweiter Zugriff liest einen leeren String und scheitert mit der Meldung von char 0:

with open("daten.json", encoding="utf-8") as f:
    data = json.load(f)
    nochmal = json.load(f)   # Expecting value: line 1 column 1 (char 0)

Dasselbe passiert, wenn du vorher f.read() aufrufst, um die Datei auszugeben, und danach json.load(f). Lies den Inhalt einmal in eine Variable oder springe mit f.seek(0) an den Anfang zurück.

Eine verwandte Falle ist Extra data: Enthält eine Datei mehrere JSON-Werte hintereinander, etwa eine Zeile pro Objekt (JSON Lines), kann json.loads nur einen Wert lesen. Lies dann zeilenweise:

with open("log.jsonl", encoding="utf-8") as f:
    records = [json.loads(line) for line in f if line.strip()]

Ähnliche Fälle in JavaScript: Unexpected non-whitespace character after JSON.

Fehler abfangen und die Stelle zeigen

Fange json.JSONDecodeError gezielt ab, nicht pauschal Exception. Mit den Attributen zeigst du die Fehlerstelle im Kontext:

import json

def parse_json(text):
    try:
        return json.loads(text)
    except json.JSONDecodeError as e:
        zeilen = text.splitlines() or [""]
        zeile = zeilen[e.lineno - 1] if e.lineno <= len(zeilen) else ""
        print(f"Ungültiges JSON: {e.msg}")
        print(f"Zeile {e.lineno}, Spalte {e.colno} (Zeichen {e.pos})")
        print(zeile)
        print(" " * (e.colno - 1) + "^")
        raise

parse_json('{\n  "a": 1,\n  "b": ,\n}')

Ausgabe:

Ungültiges JSON: Expecting value
Zeile 3, Spalte 8 (Zeichen 19)
  "b": ,
       ^

Bei json.load(f) fehlt dir der Text; nimm dann e.doc, das den geparsten Inhalt enthält. Wegen ValueError als Basisklasse fängt except ValueError ebenfalls alles ab, sagt aber weniger aus.

Zum Sonderfall Invalid control character: Ein roher Zeilenumbruch in einem String ist in JSON verboten, er muss als \n geschrieben sein. Du kannst json.loads(text, strict=False) verwenden, um solche Zeichen zu erlauben. Das gilt aber nur für Python und ist kein gültiges JSON nach RFC 8259. Besser ist es, die Quelle zu korrigieren.

Kurz gesagt

  • Expecting value: line 1 column 1 (char 0) bedeutet fast immer: leerer Text, HTML oder bereits gelesene Datei, nicht ein Fehler mitten im JSON.
  • Gib bei requests zuerst status_code und response.text[:200] aus, rufe raise_for_status() auf und parse erst dann.
  • Erzeuge JSON mit json.dumps, nie mit str(dict), und öffne Dateien bei Verdacht auf BOM mit encoding="utf-8-sig".
  • Fange json.JSONDecodeError ab und nutze msg, lineno, colno und pos, um die Stelle anzuzeigen.
  • Den Rest erledigt ein Blick auf den Text: Im JSON-Formatierer siehst du die Fehlerstelle sofort.

Weiterlesen: JSON-Fehler finden und beheben, BOM in JSON-Dateien, Anführungszeichen in JSON.