mztools · dev-tool-box · Kapitel VI & VII

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.

Warum „Doktor“?
Ein Werkzeug, das nur Daten anzeigt, überlässt die Deutung dem Benutzer. Ein Doktor-Modul deutet selbst: Es sagt nicht nur was da steht, sondern was los ist und was man jetzt tut — und zwar offline, auf Deutsch, ohne dass man Firmen-Stacktraces in eine Suchmaschine kopieren muss.

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.

außen — nur Verpackung
NestedServletException: Request processing failed
Caused by — Übersetzung durch Spring
DataIntegrityViolationException
Caused by — Übersetzung durch Hibernate
ConstraintViolationException
Caused by — ROOT CAUSE, hier steht die Wahrheit
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:

  1. Kette zerlegen: Der Parser erkennt die äußere Exception, jede Caused by:-Stufe und auch Suppressed:-Einträge (die z. B. beim Schließen von Ressourcen in try-with-resources entstehen — sie zählen bewusst nicht als Root Cause).
  2. 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.
  3. 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.
  4. 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.
Kapitel VI — stacktrace
analysefaq
root cause java.sql.SQLIntegrityConstraintViolationException
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:

ägemeint
→als UTF-8 gespeichert
C3 A4zwei Bytes
→fälschlich als Windows-1252 gelesen
äzwei Zeichen!

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:

  1. Jedes Zeichen muss überhaupt in ein Windows-1252-Byte zurückübersetzbar sein,
  2. die entstandene Bytefolge muss striktes, gültiges UTF-8 sein (kein „replace“-Modus!),
  3. 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üfungAntwort 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-VorschauDie ersten 512 Bytes roh ansehen und die Datei probeweise als UTF-8, Windows-1252, UTF-16 … dekodieren und vergleichen
Gotcha: „ISO-8859-1“ heißt fast immer Windows-1252
Die beiden unterscheiden sich genau im Bereich 0x80–0x9F — dort hat Windows-1252 die druckbaren Zeichen €, „ “, – , ™. Wer Legacy-Daten als iso-8859-1 konvertiert, verliert genau diese Zeichen. Browser (und dieses Modul) behandeln „Latin-1“ deshalb grundsätzlich als Windows-1252.

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).

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.

Frage 1 · stacktrace → analyse
Ein Spring-Stacktrace zeigt: NestedServletException → Caused by: DataIntegrityViolationException → Caused by: ConstraintViolationException → Caused by: SQLIntegrityConstraintViolationException. Welche Exception behandelt der analyse-Tab als Root Cause — und warum?
Java verkettet Exceptions von außen nach innen: Jede Schicht fängt die Ursache und verpackt sie neu. Die letzte „Caused by“ (hier: der JDBC-Fehler mit dem Constraint-Namen) ist das ursprüngliche Ereignis — deshalb rahmt der analyse-Tab genau sie rot und zeigt sie in der Diagnose zuerst. Suppressed-Einträge zählen dabei bewusst nicht als Root Cause.
Frage 2 · stacktrace → faq
Wozu existiert der faq-Tab des Stacktrace-Doktors — über eine bloße Linksammlung hinaus?
Das Doktor-Muster: Die Analyse stellt die Diagnose, die FAQ liefert die Therapie. 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.
Frage 3 · encoding → mojibake
Warum kann der mojibake-Tab „ä“ verlustfrei zu „ä“ reparieren, während bei „�“ nichts mehr zu holen ist?
Der ganze Tab beruht auf dieser Unterscheidung: Fehlinterpretation (Bytes intakt → umkehrbar, sogar über mehrere Runden: ä → ä → ä) versus Ersetzung (U+FFFD hat die Originalbytes überschrieben → unumkehrbar). Deshalb prüft jede Reparatur-Runde streng: cp1252-rückkodierbar, striktes UTF-8, sinkende Signatur-Zahl — und bei � sagt die Diagnose ehrlich „zurück zur Quelle“.
Frage 4 · encoding → text-check
Ein aus Confluence kopierter Befehl schlägt mit „command not found“ fehl, sieht im Terminal aber korrekt aus. Welcher Befund des text-check-Tabs erklärt das am wahrscheinlichsten?
Rich-Text-Editoren ersetzen beim Tippen gewöhnliche Leerzeichen gern durch geschützte (NBSP), Anführungszeichen durch typografische und -- 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.
Frage 5 · encoding → datei
Eine Datei heißt 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.
Frage 6 · encoding → faq
Ein Nutzer-Emoji 😀 lässt eine MySQL-INSERT-Anweisung mit „Incorrect string value: '\xF0\x9F\x98\x80'“ scheitern, obwohl die Spalte als „utf8“ deklariert ist. Was erklärt der passende FAQ-Eintrag?
Der Klassiker der Datenbank-Encodings: MySQLs historisches „utf8“ ist ein beschnittenes UTF-8 (max. 3 Bytes). Das Emoji 😀 ist die 4-Byte-Sequenz F0 9F 98 80 — genau die aus der Fehlermeldung. Der FAQ-Eintrag zeigt die Umstellung auf utf8mb4 samt Prüf-SQL und JDBC-Parametern und warnt: Vorhandener Mojibake bleibt auch nach der Umstellung Mojibake (→ mojibake-Tab).