LaTeX-Fehlermeldungen lesen sich deshalb so sperrig, weil sie keine Stacktraces sind. Die zwei gestapelten Zeilen unter ! Undefined control sequence erklären nicht, welcher Befehl falsch ist; sie sind eine Momentaufnahme des Lesekopfs von TeX in dem Augenblick, in dem er stehen blieb – oben steht das bereits Gelesene, unten das noch nicht Gelesene, und der Bruch dazwischen ist der Unfallort. Ist das einmal klar, erklärt derselbe Mechanismus auch, warum die Zeilennummer l.NN gelegentlich lügt, ob an der ?-Eingabe h oder x das Richtige ist und warum in der Datei .log mehr steht als je auf dem Terminal zu sehen war. Diese Seite behandelt den Aufbau einer TeX-Fehlermeldung, -file-line-error, die vier -interaction-Modi, das Lesen des Logs und das Eingrenzen der Ursache per Halbierung.
Aufbau einer TeX-Fehlermeldung: die !-Zeile und die zwei gestapelten Zeilen
Die !-Zeile sagt, was passiert ist; die beiden gestapelten Zeilen ab l.NN sagen, wo TeX stehen blieb – und der Übeltäter steht fast immer ganz rechts in der oberen Zeile. TeX zerschneidet die Eingabezeile in einen gelesenen und einen ungelesenen Teil, stapelt beide Hälften und markiert den Schnitt durch Einrückung. Im Beispiel unten endet die obere Hälfte mit \textbnf, genau dem Befehl, der beim Lesen explodierte; {bold} text. war noch unberührt und steht deshalb in der unteren Hälfte. Dieser Schnitt ist weit verlässlicher als die Zeilennummer: Die Nummer zeigt, wo TeX etwas bemerkte, der Schnitt zeigt, wo TeX war.
! Undefined control sequence.
l.3 This is \textbnf
{bold} text.
? Über l.NN erscheinen mitunter weitere Zeilen; sie bilden den Kontext des Fehlers. Eine Zeile mit ->, etwa \mynorm #1->\lVert, bedeutet, dass der Fehler innerhalb der Expansion dieses Makros auftrat. <inserted text> ist ein Token, das TeX zur Wiederherstellung selbst ergänzt hat, <to be read again> ein bereits gelesenes und wieder zurückgelegtes Token, und <read *> heißt, dass auf eine Eingabe am Terminal gewartet wird. Passt eine Zeile nicht auf das Terminal, wird ihr Anfang mit ... ausgelassen; eine Anzeige wie l.9 ...: $\frac{1}{2}$ and a stray \undefinedmacro beginnt also in Wahrheit weiter links.
| Kontextzeile | Bedeutung |
|---|---|
l.NN | die gelesene Eingabezeile; der Schnitt zwischen den Hälften ist die Stoppstelle |
\mac #1-> | trat in der Expansion von \mac auf; die Definition steht anderswo |
<inserted text> | ein von TeX selbst zur Rettung ergänztes Token, oft ein $ |
<recently read> | das eben gelesene Token, meist die Ursache selbst |
<to be read again> | ein gelesenes und zurückgelegtes Token; es wird als Nächstes erneut gelesen |
<argument> | trat innerhalb eines Arguments auf – das Argument prüfen, nicht den Aufruf |
<read *> | wartet auf Terminaleingabe; ein nicht interaktiver Modus bricht hier sofort ab |
Warum l.NN manchmal genau eine Zeile zu weit steht
Ein von \usepackage ausgelöster Fehler wird meist eine Zeile zu spät gemeldet, und schuld ist das optionale Datumsargument, das \usepackage am Ende erlaubt. Da \usepackage[opt]{pkg}[2021/02/14] zulässig ist, muss TeX über die schließende Klammer hinausschauen, um zu prüfen, ob ein [ folgt; dieser Vorgriff überspringt Leerzeichen und Zeilenenden und hat die nächste Zeile bereits eingelesen, wenn der Fehler ausgelöst wird. Auf TeX Live 2024 gemessen: Steht \usepackage[latin1]{inputenc} in Zeile 3, wird der Optionskonflikt bei l.4 gemeldet; ergänzt man am Ende derselben Zeile ein explizites [2021/02/14], wandert die Meldung auf l.3. Zeigt ein paketbezogener Fehler also auf eine Leerzeile oder auf \begin{document}, ist eine Zeile weiter oben zu suchen.
% \usepackage[latin1]{inputenc} sits on line 3 -- reported at line 4
./oc1.tex:4: LaTeX Error: Option clash for package inputenc.
l.4 \begin
{document}
% same source, but the optional date argument is written out
% -- the look-ahead ends on line 3, and so does the report
./ocA.tex:3: LaTeX Error: Option clash for package inputenc.
l.3 \usepackage[latin1]{inputenc}[2021/02/14]Dieselbe Lücke zwischen „wo TeX es bemerkte“ und „wo der Fehler steckt“ tut sich bei einer nicht geschlossenen } auf, nur reicht sie dort über Dutzende Zeilen statt über eine, und TeX gibt schließlich am Absatzende oder bei \end{document} auf. Die Einzelfälle – verlorener Mathematikmodus, undefinierte Befehle, fehlende Klammern – haben jeweils eigene Seiten. Mitzunehmen ist hier nur die allgemeine Regel: Je unschuldiger die gemeldete Zeile aussieht, desto weiter flussaufwärts liegt der Fehler.
-file-line-error: das Format, zu dem der Editor springen kann
Mit -file-line-error wird das führende ! durch ./file.tex:3: ersetzt, wodurch Dateiname und Zeilennummer in einer Zeile stehen und ein Editor oder ein CI-Logparser direkt dorthin springen kann. Das Standardformat hat eine echte Lücke: l.3 liefert nur eine Zahl, den Dateinamen muss man aus einer weit oben gedruckten öffnenden Klammer wie (./chapters/intro.tex erschließen. In einem Dokument, das mit \input in Kapitel zerlegt ist, kostet genau dieses Erschließen die Zeit. -file-line-error beseitigt es, und die zwei gestapelten l.NN-Zeilen erscheinen weiterhin – verloren geht also nichts.
$ pdflatex main.tex
(./chapters/intro.tex
! Undefined control sequence.
l.2 Here is \nosuchcmd
in a chapter.
$ pdflatex -file-line-error main.tex
(./chapters/intro.tex
./chapters/intro.tex:2: Undefined control sequence.
l.2 Here is \nosuchcmd
in a chapter.In vielen Umgebungen ist dieses Format ohnehin voreingestellt: latexmk schaltet es intern ein, und Oberflächen wie TeXworks oder LaTeX Workshop für VS Code ergänzen es von sich aus. Beim Aufruf von Hand gehört -file-line-error dazu, zum ausdrücklichen Abschalten -no-file-line-error. Ein nützlicher Nebeneffekt: Stammt der Fehler aus einem Paket, ist der angezeigte Pfad dessen eigene Datei – eine Zeile /usr/local/texlive/…/foo.sty:120: bedeutet, dass sich foo beschwert und nicht etwas selbst Geschriebenes.
Die ?-Eingabe: h, i, x, q, r, s und Return
An der ?-Eingabe sind neun Antworten möglich, und ein eingetipptes ? lässt TeX die Liste selbst ausgeben. Das ist das Verhalten des voreingestellten errorstopmode, in dem TeX buchstäblich nachfragt, wie weiter verfahren werden soll. Drei Antworten tragen den größten Teil des Verkehrs: Return (diesen Fehler übergehen und fortfahren), h (TeXs eigenen Hilfetext zur Meldung ausgeben) und x (den Lauf sofort abbrechen, ohne PDF). Sind in einem langen Dokument weitere Fehler zu erwarten, ist der schnellste Weg r oder s, den Lauf zu Ende laufen zu lassen und danach die .log zu lesen.
? ?
Type <return> to proceed, S to scroll future error messages,
R to run without stopping, Q to run quietly,
I to insert something, E to edit your file,
1 or ... or 9 to ignore the next 1 to 9 tokens of input,
H for help, X to quit.
?| Antwort | Was TeX tut |
|---|---|
Return | den Fehler vergessen und fortfahren; der Satz läuft weiter, ein PDF entsteht trotzdem |
h | den Hilfetext zu dieser Meldung ausgeben; in der .log steht er ohnehin |
i | an dieser Stelle Text einfügen – i\textbf korrigiert einen Tippfehler nur für diesen Lauf |
x | den Lauf sofort abbrechen; es erscheint No pages of output., kein PDF wird geschrieben |
q | gibt OK, entering \batchmode aus und beendet den Lauf schweigend |
r | gibt OK, entering \nonstopmode... aus und läuft ohne Halt bis zum Ende |
s | gibt OK, entering \scrollmode... aus; hält nicht an, liest aber weiterhin vom Terminal |
e | den in der Umgebungsvariablen TEXEDIT genannten Editor an dieser Zeile öffnen |
1 … 9 | die nächsten 1 bis 9 Tokens verwerfen und fortfahren; die Zeile wird am neuen Schnitt erneut gezeigt |
Die vier -interaction-Modi und wann welcher passt
pdflatex --help nennt vier Werte – batchmode, nonstopmode, scrollmode, errorstopmode – und die Voreinstellung ist errorstopmode. Für skriptgesteuerte Läufe eignet sich -interaction=nonstopmode, für CI-Jobs, die das Terminal nicht fluten sollen, -interaction=batchmode. Die vier unterscheiden sich nur in zwei Achsen: ob angehalten und ob auf das Terminal geschrieben wird. Am häufigsten missverstanden wird das Paar scrollmode gegen nonstopmode. Gemessen: Ein Dokument mit \typein liest die Antwort unter scrollmode tatsächlich vom Terminal und bricht unter nonstopmode mit ! Emergency stop. ab. Die Trennlinie verläuft nicht bei Fehlern, sondern bei der Terminaleingabe.
| Modus | Hält an? Schreibt aufs Terminal? |
|---|---|
errorstopmode | Voreinstellung; hält bei jedem Fehler mit ? an – passend fürs Arbeiten von Hand |
scrollmode | hält bei Fehlern nicht an, liest aber weiter vom Terminal; gut zum Überfliegen eines ganzen Laufs |
nonstopmode | liest nie vom Terminal; wird Eingabe verlangt, endet der Lauf mit ! Emergency stop. |
batchmode | nonstopmode zuzüglich unterdrückter Terminalausgabe; die .log wird dennoch vollständig geschrieben |
Dass batchmode „nichts“ ausgibt, stimmt fast. Wird dasselbe fehlerhafte Dokument auf TeX Live 2024 vermessen, erhält das Terminal unter nonstopmode 1212 Bytes und unter batchmode 144 Bytes – übrig bleiben das pdfTeX-Banner und entering extended mode, beide gedruckt, bevor der Interaktionsmodus greift. Die .log ist in beiden Fällen 4144 Bytes groß, Byte für Byte identisch, und das PDF entsteht so oder so. batchmode wirft also keine Information weg, es hält sie nur vom Terminal fern. Daraus folgt das übliche CI-Rezept: im Batch-Modus laufen lassen, über den im nächsten Abschnitt behandelten Rückgabewert entscheiden und die .log für die Einzelheiten archivieren. Alle vier Namen sind zugleich TeX-Primitive, ein \nonstopmode am Dateianfang wirkt also genauso.
-halt-on-error und der Rückgabewert
-halt-on-error bricht den Lauf beim ersten Fehler ab. Auf TeX Live 2024 geprüft: unmittelbar nach dem ersten ! Undefined control sequence erscheint ! ==> Fatal error occurred, no output PDF file produced!, der Lauf endet, und es bleibt kein PDF zurück. Dasselbe Dokument unter bloßem -interaction=nonstopmode meldet alle vier Fehler und schreibt trotzdem ein PDF – die Option ist also genau dann richtig, wenn ein kaputtes Dokument nicht wie ein gelungener Build aussehen darf. Auch der Rückgabewert wurde gemessen: 1, sobald irgendein Fehler auftrat, sonst 0. Das hängt nicht vom Modus ab, nonstopmode und batchmode verhalten sich gleich, und Warnungen ändern daran nichts. Eine Kette pdflatex && … in einem Makefile oder CI-Job bricht folglich nur bei Fehlern ab.
# interactive: stop at the first error and look at it
pdflatex -file-line-error -halt-on-error document.tex
# CI: quiet terminal, full log on disk, exit status decides
pdflatex -interaction=batchmode -halt-on-error -file-line-error document.tex
echo $? # 1 if any error occurred, 0 if noneDie .log lesen: darin steht mehr, als das Terminal je zeigte
In der .log steht der Hilfetext, den das Terminal nie ausgegeben hat; ergibt eine Meldung keinen Sinn, muss man sie also nicht noch einmal erzeugen und h tippen, sondern nur das Log öffnen. In einem gemessenen Lauf erreichten 938 Bytes das Terminal, die .log enthielt 3199 – der Unterschied besteht größtenteils aus eben diesem Hilfetext. Am deutlichsten zeigt sich das beim Optionskonflikt: Auf dem Terminal erscheint nur ! LaTeX Error: Option clash for package inputenc., während das Log ausschreibt, mit welchen Optionen das Paket zuerst geladen wurde und welche nun verlangt werden. Diese vier Zeilen unterscheiden Raten von Wissen.
% only the first line of this reaches the terminal
./oc1.tex:4: LaTeX Error: Option clash for package inputenc.
...
The package inputenc has already been loaded with options:
[utf8]
There has now been an attempt to load it with options
[latin1]
Adding the global options:
utf8,latin1
to your \documentclass declaration may fix this.Auch die Gestalt des gesamten Logs zu kennen zahlt sich aus. Die erste Zeile nennt Engine, Version sowie Datum und Uhrzeit des Laufs; die nächste zeigt den Aufruf als **document.tex; danach folgen nur noch verschachtelte Klammern – ( öffnet eine Datei, ) schließt sie, und damit beantwortet die Verschachtelung die Frage, welche Datei ein Paket hereingeholt hat. [1], [2] markieren ausgegebene Seiten, den Schluss bildet die Speicherbilanz nach Here is how much of TeX's memory you used: samt Output written on document.pdf (1 page, 12817 bytes).. Wer sich das nicht antun will, schickt den Lauf durch das mit TeX Live gelieferte texfot, das die Ausgabe auf Fehler, Warnungen und die abschließende Zusammenfassung eindampft.
$ texfot pdflatex -interaction=nonstopmode two.tex
texfot: invoking: pdflatex -interaction=nonstopmode two.tex
This is pdfTeX, Version 3.141592653-2.6-1.40.26 (TeX Live 2024)
! Undefined control sequence.
l.3 First \foo
! Missing $ inserted.
<inserted text>
l.4 Second \bar
Output written on two.pdf (1 page, 23091 bytes).\listfiles und der *File List*-Block: zählen, was wirklich geladen wurde
Ein einzelnes \listfiles irgendwo in der Präambel ergänzt die .log am Ende um eine Tabelle *File List*, die jede geladene Datei mit Datum, Version und Kurzbeschreibung aufführt. Auf TeX Live 2024 gezählt: Ein nacktes article lädt 3 Dateien (article.cls, size10.clo, l3backend-pdftex.def). Eine einzige Zeile hyperref macht daraus 33 – hyperref allein zieht also 30 weitere nach sich. Bei tikz sind es 34. Das ist der erste Griff, sobald an einem Konflikt ein Paket beteiligt ist, das nie angefordert wurde. Und es ist genau das, was man beim Stellen einer Frage oder in einem Fehlerbericht einfügt: Die Tabelle macht Unterschiede zwischen zwei Installationen auf einen Blick sichtbar.
% \listfiles goes anywhere in the preamble; this lands at the end of the .log
*File List*
article.cls 2023/05/17 v1.4n Standard LaTeX document class
size10.clo 2023/05/17 v1.4n Standard LaTeX file (size option)
amsmath.sty 2023/05/13 v2.17o AMS math features
hyperref.sty 2024-01-20 v7.01h Hypertext links for LaTeX
iftex.sty 2022/02/03 v1.0f TeX engine tests
***********Für eine feinere Sicht kommt -recorder hinzu. Jede während des Laufs geöffnete Datei wird als INPUT-Zeile in eine .fls-Datei geschrieben – ein Dokument, dessen einziges Paket tikz ist, erzeugt davon 140. Während \listfiles beantwortet, welche Pakete geladen wurden, beantwortet .fls, welche Dateien überhaupt berührt wurden, bis hin zu .tfm-Schriftdateien und Konfigurationsdateien. Beim Verfolgen eines Paketkonflikts ist das Erste richtig, beim Verfolgen der Suchpfade von kpathsea das Zweite.
\show, \showthe, \typeout: ausgeben, was TeX gerade glaubt
\show\foo gibt die Definition von \foo aus, \showthe\textwidth den Wert einer Länge oder eines Zählers. Die Ausgabe landet in der .log als > \LaTeX=macro: oder > 345.0pt. – das führende > ist die Markierung, und 345,0 pt ist zufällig die voreingestellte \textwidth von article. Wenn unklar ist, wie ein Befehl gerade definiert ist, schlägt \show jedes Raten, und meist klärt sich damit auch, ob die Klasse oder ein Paket umdefiniert hat. Für eigene Meldungen gibt es \typeout{…} und \message{…}; gemessen setzt \typeout seinen Text in eine eigene Zeile, \message hängt ihn an die laufende Zeile an. Das Erste liest sich beim printf-artigen Debuggen besser, das Zweite eignet sich, um eine Stelle neben einer Seitenzahl zu markieren.
\show\LaTeX % > \LaTeX=macro: ... (definition follows)
\showthe\textwidth % > 345.0pt. (article default)
\typeout{reached the theorem} % own line in log and terminal
\message{mark} % appended to the current line
\tracingall % dump every step to the log -- extremely verboseDas letzte Mittel ist \tracingall: Es schreibt jeden Schritt von TeX ins Log – Makroexpansionen, Moduswechsel, versuchte Zeilenumbrüche. Schon bei wenigen Seiten kommen zweistellige Megabytezahlen zusammen, deshalb sollte es unmittelbar vor der fraglichen Stelle eingeschaltet und direkt danach mit \tracingnone wieder abgeschaltet werden, oder man kombiniert es mit dem Paket trace, das die Ausgabe lesbar aufbereitet. \tracingall beantwortet, in welcher Reihenfolge etwas geschah, nicht welches Makro schuld ist – und steht die Reihenfolge fest, klärt ein einzelnes \show meist den Rest.
Das Dokument halbieren: \end{document} nach oben schieben
Reicht die Meldung allein nicht, ist das Halbieren des Dokuments der kürzeste Weg: ein zusätzliches \end{document} mitten im Text, und alles dahinter wird ignoriert. Auf TeX Live 2024 geprüft – was nach \end{document} folgt, selbst ein kaputter Befehl, wird nie gelesen. Das ursprüngliche muss also nicht einmal entfernt werden; es genügt, die eingefügte Zeile auf und ab zu schieben und den Fehler von beiden Seiten einzukreisen. Zehn Verschiebungen engen ein Dokument von tausend Zeilen auf eine einzige ein. Steht die Präambel im Verdacht, wird jeweils die Hälfte der \usepackage-Zeilen mit % auskommentiert; sind die Kapitel per \include getrennt, führt \includeonly{chapter3} zum Ziel.
\begin{document}
\input{chapters/intro}
\input{chapters/method}
\end{document} % <- added: bisect here, everything below is ignored
\input{chapters/results}
\input{chapters/discussion}
\end{document}Ist die Hälfte erreicht, wird bis zum kleinsten noch fehlschlagenden Rest weitergekürzt. \usepackage-Zeilen einzeln entfernen, Fließtext absatzweise streichen, Abbildungen durch das mit graphicx gelieferte example-image und lange Passagen durch lipsum ersetzen – übrig bleibt meist gut ein Dutzend Zeilen. In dieser Größe liegt die Ursache in aller Regel offen zutage; und falls nicht, sind genau diese Zeilen das, was in eine Frage gehört. Das Kürzen ist bereits die Diagnose – wie man gut fragt und wo, behandelt die Seite zur Community.