Der Stacktrace-Doktor und der Encoding-Doktor
Was die beiden neuen Module tun, warum jeder Untermenüpunkt existiert — und ein Quiz zum Selbsttest.
1 · Hintergrund: die Doktor-Idee in mztools
(Wer mztools schon kennt, kann direkt zu Abschnitt 2 springen.)
mztools ist eine offline laufende Entwickler-Werkzeugkiste: ein portables Windows-Programm, in dem jedes Werkzeug ein „Kapitel“ in der Seitenleiste ist — Regex, JWT, Zertifikate, Keystores, Hashes und so weiter. Zwei Grundsätze ziehen sich durch alle Module: nichts verlässt den Rechner (kein Cloud-Upload, kein Logging von Inhalten), und die anspruchsvolleren Module folgen einem „Doktor“-Muster: Man wirft dem Modul ein kaputtes Artefakt hin, es stellt eine Diagnose in verständlichem Deutsch und verlinkt auf eine kuratierte FAQ mit Ursache, Lösung und fertigen Befehlen. Das Chain-Modul (Kapitel V) macht das seit längerem für Zertifikatsketten.
Die beiden neuen Kapitel übertragen dieses Muster auf zwei Alltagsprobleme, die jeder Java-Entwickler kennt: unlesbare Stacktraces und kaputte Umlaute. Beide Module sind reine Frontend-Module — sie brauchen keinen Server, alles passiert im Browser-Teil der App.
2 · Stacktrace-Doktor (Kapitel VI)
Das Problem
Ein echter Java-Stacktrace aus einer Spring-Anwendung ist leicht 150 Zeilen lang. Davon stammen
vielleicht vier Zeilen aus eigenem Code — der Rest ist Servlet-Container, Framework-Plumbing,
Reflection und Proxys. Und die wichtigste Information steht ausgerechnet ganz unten: Java
verkettet Exceptions über Caused by:, und die letzte „Caused by“ ist die
eigentliche Ursache. Die äußerste Exception, die einem als Erstes ins Auge springt, ist fast immer
nur Verpackung.
NestedServletException: Request processing failed
DataIntegrityViolationException
ConstraintViolationException
SQLIntegrityConstraintViolationException:
Duplicate entry 'RE-2026-0815' for key 'invoice.uk_invoice_number'
Die Diagnose steckt komplett in der untersten Box: Es gibt einen Unique-Constraint
uk_invoice_number, und jemand hat versucht, die Rechnungsnummer RE-2026-0815
ein zweites Mal einzufügen. Alles darüber ist dieselbe Geschichte, dreimal umverpackt.
Untermenüpunkt analyse — Sinn & Zweck
Der analyse-Tab automatisiert genau dieses „von unten lesen“. Man fügt einen Stacktrace
ein (oder lädt das eingebaute Beispiel), und das Modul tut vier Dinge:
- Kette zerlegen: Der Parser erkennt die äußere Exception, jede
Caused by:-Stufe und auchSuppressed:-Einträge (die z. B. beim Schließen von Ressourcen in try-with-resources entstehen — sie zählen bewusst nicht als Root Cause). - Root Cause hervorheben: Die letzte nicht-unterdrückte Exception wird rot gerahmt und in der Diagnose-Karte zuerst genannt — samt erster Zeile ihrer Message, denn dort steht (etwa bei den „Helpful NullPointerExceptions“ seit Java 14) oft die halbe Lösung.
- Rauschen falten: Frames aus
java.*,org.springframework.*, Tomcat, Hibernate, JDBC-Treibern, CGLIB-Proxys ($$) und Reflection werden zu einer klickbaren Zeile „▸ 12 framework-frames …“ zusammengelegt. Eigener Code bleibt sichtbar. - Ursprung markieren: Der erste Nicht-Rausch-Frame der Root Cause bekommt ein ⭑ — „vermutlich dein Code“ — mit Klasse, Methode und Zeilennummer. Das ist die Stelle, an der man zu debuggen beginnt.
Duplicate entry 'RE-2026-0815' for key 'invoice.uk_invoice_number'
⭑ vermutlich dein Code: InvoiceRepository.insertRow(InvoiceRepository.java:74)
4 exceptions in der kette 21 frames · 15 framework
faq: SQLException-Familie — Constraint, Syntax, Verbindung →
Untermenüpunkt faq — Sinn & Zweck
Die FAQ ist die Wissensbasis hinter der Diagnose: dreizehn deutsche Einträge zu den Exception-Klassikern — NullPointerException (und wie man die Helpful Messages liest), ClassNotFound vs. NoClassDefFound, NoSuchMethodError als Dependency-Konflikt, UnsupportedClassVersionError mit der Class-File-Versionstabelle, die OutOfMemory-Varianten, StackOverflow, ConcurrentModification, SSL/PKIX, Netzwerkfehler, Encoding-Fehler, Parse-/Locale-Fallen, Spring-Bean-Fehler und die SQLException-Familie. Jeder Eintrag folgt demselben Schema: Symptome → Ursache → Lösung → fertige Befehle zum Kopieren.
Der Clou ist die Verzahnung: Erkennt die Analyse den Typ der Root Cause, blendet sie direkt einen
Button zum passenden FAQ-Eintrag ein — aus „Duplicate entry …“ wird ein Klick zu „SQLException-Familie“.
Die FAQ ist außerdem im Volltext durchsuchbar und per Deep-Link erreichbar
(#stacktrace/faq), taugt also auch als eigenständiges Nachschlagewerk.
3 · Encoding-Doktor (Kapitel VII)
Das Problem, von den Bytes her gedacht
(Kurzer Grundlagen-Absatz — Encoding-Kenner springen zum nächsten Untertitel.)
Text existiert im Rechner nur als Bytes. Ein Encoding ist die Vereinbarung, welche Bytes
welches Zeichen bedeuten. In UTF-8 ist das ä ZWEI Bytes (C3 A4); im
alten Windows-1252 ist jedes Byte genau ein Zeichen. Genau daraus entsteht der berühmteste
Textunfall überhaupt:
Wichtig ist die Einsicht, dass hierbei nichts verloren geht: Die Bytes sind noch
alle da, sie wurden nur falsch interpretiert. Deshalb ist dieser Unfall — im Gegensatz zu
seinem bösen Bruder, dem Ersatzzeichen � — vollständig umkehrbar. Auf dieser Einsicht
baut der wichtigste Tab des Moduls auf.
Untermenüpunkt mojibake — Sinn & Zweck
Der mojibake-Tab repariert solchen „ä-Salat“ (japanisch mojibake =
verwandelte Schrift). Man fügt den kaputten Text ein; das Modul rechnet den Unfall rückwärts:
sichtbare Zeichen → zurück in ihre Windows-1252-Bytes → diese Bytes korrekt als UTF-8 dekodieren.
Damit dabei nie geraten wird, muss jede Reparatur-Runde drei Prüfungen bestehen:
- Jedes Zeichen muss überhaupt in ein Windows-1252-Byte zurückübersetzbar sein,
- die entstandene Bytefolge muss striktes, gültiges UTF-8 sein (kein „replace“-Modus!),
- und die Zahl der Mojibake-Signaturen (Ã/Â/â + Folgezeichen) muss dabei sinken.
Schlägt eine Prüfung fehl, wird die Runde verworfen — das Modul verschlimmbessert nie. Weil der
Unfall auch mehrfach passieren kann (kaputt gespeichert, wieder falsch gelesen, wieder gespeichert:
aus ä wird ä, dann ä), läuft die Reparatur iterativ bis zu
vier Runden. Und sie arbeitet zeilenweise — denn echte Log-Auszüge mischen saubere
und kaputte Zeilen, und eine globale Prüfung würde an den sauberen Zeilen scheitern. Enthält der Text
�, sagt die Diagnose ehrlich: Hier wurde Information ersetzt, das ist nicht reparierbar —
zurück zur Quelle (dazu gleich der datei-Tab).
Untermenüpunkt text-check — Sinn & Zweck
Der zweite klassische Textunfall ist unsichtbar: Text, der richtig aussieht, aber nicht
funktioniert. Wer einen Befehl aus Confluence, Word oder Teams kopiert, bekommt gern ein geschütztes
Leerzeichen (NBSP, U+00A0) statt eines echten Spaces, typografische Anführungszeichen statt gerader,
einen Gedankenstrich statt -- — oder komplett unsichtbare Zero-Width-Zeichen. Die Shell
sagt dann „command not found“, YAML wirft kryptische Parserfehler, und zwei „identische“ Strings sind
ungleich.
Der text-check-Tab macht diese Zeichen sichtbar: eine Befundtabelle mit Codepoint,
Anzahl, Position (Zeile:Spalte) und deutscher Erklärung, nach Schweregrad sortiert — bis hin zu
Bidi-Steuerzeichen, mit denen sich Quellcode bewusst tarnen lässt (Trojan Source). Dazu gibt es
Quick-Fixes als Checkboxen (NBSP→Space, Unsichtbare entfernen, Anführungszeichen begradigen,
Striche→Minus) und den bereinigten Text zum Kopieren.
Untermenüpunkt datei — Sinn & Zweck
Wenn Text- und Mojibake-Fragen auftauchen, lautet die Grundfrage oft: Was IST diese Datei
eigentlich? Der datei-Tab beantwortet sie auf Byte-Ebene, ohne dass die Datei den
Rechner verlässt:
| Prüfung | Antwort auf … |
|---|---|
| BOM-Erkennung (UTF-8, UTF-16 LE/BE, UTF-32) | „Warum beginnt meine erste CSV-Spalte mit Müll?“ / „Warum bricht das Shell-Skript in Zeile 1?“ |
| Strikte UTF-8-Validierung | „Ist das echtes UTF-8 — oder Legacy-Windows-1252?“ |
| Zeilenenden-Zählung (CRLF / LF / CR) | „Warum zeigt Git jede Zeile als geändert?“ — gemischte Enden werden rot markiert |
| Magic Bytes | „Die Endung lügt — was ist es wirklich?“ (PNG, ZIP/JAR, PDF, GZIP, Java-Class CAFEBABE, sogar JKS/JCEKS-Keystores mit Verweis aufs chain-Modul) |
| Hexdump + Dekodier-Vorschau | Die ersten 512 Bytes roh ansehen und die Datei probeweise als UTF-8, Windows-1252, UTF-16 … dekodieren und vergleichen |
Untermenüpunkt faq — Sinn & Zweck
Wie beim Stacktrace-Doktor bündelt die FAQ das Hintergrundwissen, das die Tabs anwenden: zehn
deutsche Einträge von Mojibake und � über BOM, CRLF/LF (mit fertiger
.gitattributes), NBSP & unsichtbare Zeichen, typografische Anführungszeichen,
Windows-1252 vs. ISO-8859-1, Java & Encoding (JEP 400: seit Java 18 ist UTF-8 überall Default!),
MySQLs utf8-Lüge (drei Bytes — Emojis brauchen utf8mb4) bis zu Umlauten in
ZIP-Dateinamen und NFC/NFD. Jeder Eintrag verweist auf den Tab, der das Problem konkret löst.
4 · Blick in den Code
Beide Module sind reine Frontend-Module unter frontend/src/modules/; die Logik steckt
bewusst in UI-freien Bibliotheksdateien, die headless getestet werden (44 Tests).
stacktrace/lib/parse.ts— der Parser klassifiziert Zeile für Zeile (at …-Frame,Caused by:,Suppressed:,… N more, mehrzeilige Messages) und versteht auch Modul-Präfixe wiejava.base/java.lang.Thread.run. Rauschen wird über Paket-Präfixlisten plus Marker ($$,$Proxy,GeneratedMethodAccessor) erkannt;matchFaq()bildet die Root Cause auf einen FAQ-Eintrag ab.encoding/lib/mojibake.ts— enthält die Windows-1252-Tabelle (nur der Bereich 0x80–0x9F weicht von Latin-1 ab) und die dreifach abgesicherte, zeilenweise Reparatur-Schleife. Pointe: Signatur-Regex und Beispieltexte werden programmatisch aus der Byte-Tabelle erzeugt statt als Literale getippt — Mojibake-Literale im Quellcode sind selbst encodingsanfällig, das wäre der Bock als Gärtner.encoding/lib/inspect.ts— die Zeichen-Steckbriefe (Codepoint → Name, Schweregrad) und die Quick-Fix-Regexes; alle unsichtbaren Zeichen ausschließlich als\u-Escapes.encoding/lib/fileinfo.ts— BOM- und Magic-Byte-Erkennung, strikte UTF-8-Prüfung perTextDecoder(…, {fatal: true}), Zeilenenden-Statistik, UTF-16-Heuristik über NUL-Byte-Verteilung und der klassische Hexdump.- Gemeinsame Basis:
components/FaqList.tsx— die durchsuchbare FAQ-Liste wurde aus dem chain-Modul extrahiert, damit alle drei Doktoren dieselbe FAQ-Mechanik teilen.
5 · Quiz: sechs Fragen, eine pro Untermenüpunkt
Mittelschwer — wer die Abschnitte oben gelesen hat, schafft alle sechs. Klick auf eine Antwort zeigt sofort Auflösung und Begründung.
matchFaq() bildet den Typ der Root Cause (NPE, NoClassDefFound, OOM, PKIX, SQL …) auf einen der dreizehn deutschen Einträge ab — ein Klick, und man steht bei Ursache, Lösungsschritten und kopierfertigen Befehlen. Alles offline: Firmen-Stacktraces wandern nicht in Suchmaschinen.-- durch Gedankenstriche. Optisch identisch, für Shell/YAML/Compiler aber fremde Zeichen. Der text-check-Tab listet jedes davon mit Codepoint und Position — und die Quick-Fixes (NBSP→Space, Begradigen) liefern den lauffähigen Text zum Kopieren. CRLF wäre plausibel, erzeugt aber typischerweise '\r': command not found — eine andere Signatur.daten.txt, beginnt aber mit den Bytes 50 4B 03 04. Was sagt der datei-Tab — und worauf stützt er sich?50 4B ist „PK“ — die Initialen von Phil Katz, dem ZIP-Erfinder. Viele Formate sind in Wahrheit ZIPs (jar, war, docx, xlsx). Der datei-Tab prüft Magic Bytes vor allen Encoding-Fragen, denn eine Binärdatei als Text zu „reparieren“ wäre Unsinn; dieselbe Mechanik erkennt auch CAFEBABE (Java-Class) und FEEDFEED (JKS-Keystore, mit Verweis aufs chain-Modul). Dass die Bytes zufällig gültiges ASCII sind, macht die Datei nicht zu Text.