YAML: „could not find expected ':'“ und „did not find expected key“ verstehen

could not find expected ':' heißt: Der YAML-Parser hat eine Zeile als Schlüssel gelesen, aber dahinter keinen Doppelpunkt gefunden. Meist fehlt der Doppelpunkt wirklich, oder ein Text läuft ohne Einrückung in der nächsten Zeile weiter. did not find expected key ist die Schwester-Meldung: Der Parser erwartet an einer Stelle einen neuen Schlüssel und findet etwas anderes, oft wegen falscher Einrückung.

Beide Meldungen stammen aus libyaml und tauchen deshalb in vielen Werkzeugen auf. Die Werkzeuge von formatierer.de zeigen statt des libyaml-Texts eine eigene deutsche Meldung mit Zeile und Spalte. Zum Eingrenzen fügst du den Text in den YAML-Formatierer ein; mit dem YAML-zu-JSON-Konverter siehst du außerdem, wie der Parser die Struktur versteht, sobald sie gültig ist. Beides läuft lokal im Browser, formatierer.de sendet nichts an einen Server.

So sieht die Meldung aus

Ein Beispiel mit PyYAML (getestet mit PyYAML 6.0.3):

name: Ada
rolle
port: 80
while scanning a simple key
  in "<unicode string>", line 2, column 1:
    rolle
    ^
could not find expected ':'
  in "<unicode string>", line 3, column 1:
    port: 80
    ^

Die Meldung hat zwei Teile:

  • Kontextzeile (while scanning a simple key): Was der Parser gerade tat und wo er damit begonnen hat. Hier: Zeile 2, Spalte 1, also bei rolle.
  • Eigentliche Meldung (could not find expected ':') mit der Stelle, an der er aufgegeben hat: Zeile 3. Das ist die Zeile nach dem Problem.

Der Fehler liegt also fast immer in der Zeile, die in der Kontextzeile genannt wird, nicht in der letzten Positionsangabe.

Wortlaut je nach Werkzeug

Die Texte kommen aus libyaml (C) oder aus der reinen Python-Variante von PyYAML. Beim selben Fehler weichen sie ab. Lokal getestet mit PyYAML 6.0.3:

Fehler SafeLoader (reines Python) CSafeLoader (libyaml)
Zeile ohne Doppelpunkt could not find expected ':' could not find expected ':'
Falsch eingerückter Listeneintrag expected <block end>, but found '-' did not find expected key
Zwei Doppelpunkte in einer Zeile mapping values are not allowed here mapping values are not allowed in this context

Außerdem zeigt die C-Variante keinen Auszug der Zeile, nur Zeile und Spalte. Ruby (Psych), kubectl, Helm und andere Werkzeuge verpacken die Meldung oft in eigenen Text, zum Beispiel mit Dateiname oder Zeilennummer. Ob und wie genau sie sie wiedergeben, hängt von Version und Bibliothek ab. Such nach dem Kern der Meldung, nicht nach dem exakten Satz.

Ursache 1: Text über mehrere Zeilen ohne Einrückung

beschreibung: Das ist ein
langer Text
port: 80

Die zweite Zeile ist nicht eingerückt, also liest der Parser langer Text als neuen Schlüssel – und findet keinen Doppelpunkt. Gemeldet wird Zeile 2 als Kontext, Zeile 3 als Fundstelle.

Lösung 1: Folgezeilen einrücken. Das ist gültiges YAML, die Zeilen werden mit einem Leerzeichen verbunden:

beschreibung: Das ist ein
  langer Text
port: 80

Lösung 2: einen Block-Skalar verwenden. | behält Zeilenumbrüche, > faltet sie zu Leerzeichen:

beschreibung: >
  Das ist ein
  langer Text
port: 80

Mehr dazu im Ratgeber Mehrzeilige Strings in YAML.

Ursache 2: Doppelpunkt vergessen

name: Ada
rolle
port: 80

rolle steht allein. Entweder fehlt : Wert, oder die Zeile gehört gar nicht hierher, etwa ein Kommentar ohne # oder eine Notiz, die beim Kopieren mitgekommen ist.

name: Ada
rolle: admin
port: 80

Der Doppelpunkt muss direkt nach dem Schlüssel stehen und von einem Leerzeichen (oder dem Zeilenende) gefolgt werden. Ein Schlüssel wie a:b ist deshalb kein Mapping, sondern ein einziger Text.

Ursache 3: Falsch eingerückte Listeneinträge

server:
  hosts:
    - a
  - b

- b steht eine Ebene zu weit links: Es gehört weder zu hosts noch zu server als Liste. Der Parser erwartet an dieser Stelle einen weiteren Schlüssel von server. PyYAML mit libyaml meldet dann did not find expected key, die reine Python-Variante expected <block end>, but found '-'.

server:
  hosts:
    - a
    - b

Alle Einträge einer Liste haben dieselbe Einrückung. Ein Eintrag, der weiter eingerückt ist als der vorige, wird dagegen oft nicht als Fehler gemeldet, sondern als Fortsetzung des Texts gelesen, und das Ergebnis ist dann still falsch. Prüfe deshalb auch gültige Dateien, indem du sie nach JSON umwandelst.

Ursache 4: Nicht geschlossene oder verschachtelte Anführungszeichen

msg: "He said "hi" now"
port: 80

Das Anführungszeichen vor hi beendet den Text. Danach steht hi ohne Doppelpunkt. PyYAML meldet hier did not find expected key (reines Python: expected <block end>, but found '<scalar>'). Lösung: innere Anführungszeichen mit Backslash maskieren oder andere Anführungszeichen verwenden.

msg: "He said \"hi\" now"
alt: 'He said "hi" now'

Fehlt das schließende Anführungszeichen ganz, lautet die Meldung meist anders, nämlich while scanning a quoted scalar … found unexpected end of stream. Dann suchst du das öffnende Zeichen, das in der Kontextzeile genannt wird. In einfachen Anführungszeichen verdoppelst du ein Apostroph: 'it''s'.

Ursache 5: Templates wie Helm {{ }}

Helm-Templates sind kein YAML, sondern Text, aus dem erst Helm YAML erzeugt. Wenn du eine Template-Datei direkt an einen YAML-Parser gibst, schlägt er an den Klammern fehl:

image: {{ .Values.image }}:{{ .Values.tag }}

Für YAML beginnt { ein Flow-Mapping; {{ ist ein Flow-Mapping, dessen Schlüssel wiederum ein Mapping ist. Mit PyYAML (libyaml) ergibt das hier did not find expected key. Bei anderen Konstruktionen sind die Meldungen anders: name: {{ .Values.name }} liefert found unhashable key, und eine Zeile wie {{- if .Values.x }} meldet did not find expected node content.

Was du tun kannst:

  • Prüfe das gerenderte Ergebnis, nicht das Template: helm template ./chart gibt das fertige YAML aus.
  • Wenn der Fehler erst nach dem Rendern auftritt, liegt es oft an Einrückung. Nutze nindent statt indent und toYaml, und setze Werte mit Sonderzeichen in Anführungszeichen, entweder direkt im Template oder mit der Funktion quote:
image: "{{ .Values.image }}:{{ .Values.tag }}"
name: {{ .Values.name | quote }}
resources:
  {{- toYaml .Values.resources | nindent 2 }}

Das Beispiel ist Template-Text und kein gültiges YAML; gültig wird es erst nach dem Rendern. Gerenderte Ausgabe kannst du in den YAML-Formatierer einfügen.

So gehst du vor

  1. Lies die Kontextzeile und gehe zu der dort genannten Zeile und Spalte. Das ist der Anfang des Problems.
  2. Prüfe diese Zeile auf fehlenden Doppelpunkt, fehlende Einrückung und Anführungszeichen.
  3. Prüfe die Zeile darüber: Setzt dort ein Text fort, der eingerückt sein müsste?
  4. Verwende Leerzeichen, keine Tabs. Tabs als Einrückung sind in YAML nicht erlaubt und führen zu einer anderen Meldung, siehe YAML: found character that cannot start any token.
  5. Wandle die reparierte Datei mit dem YAML-zu-JSON-Konverter um und prüfe, ob die Struktur deiner Absicht entspricht.

Kurz gesagt

  • could not find expected ':': Der Parser hat eine Zeile als Schlüssel gelesen und keinen Doppelpunkt gefunden. Schau in die Zeile, die die Kontextzeile nennt.
  • did not find expected key (libyaml) bzw. expected <block end>, but found … (reines PyYAML) bedeutet meist falsche Einrückung oder ein überzähliges Anführungszeichen.
  • Mehrzeilige Texte brauchen Einrückung der Folgezeilen oder einen Block-Skalar mit | oder >.
  • Helm-Templates erst mit helm template rendern, dann das Ergebnis prüfen.
  • Der genaue Wortlaut hängt von Parser und Version ab.

Weiterlesen: YAML-Fehler im Überblick, „found character that cannot start any token“, Mehrzeilige Strings in YAML, Was ist YAML?, YAML formatieren, YAML zu JSON.