Fehler sind nur SQLite, das dir etwas mitteilen will
Die Fehlermeldungen von SQLite sind kurz und wirken manchmal kryptisch, lassen sich aber auf eine überschaubare Anzahl typischer Ursachen zurückführen. Im Produktivbetrieb tauchen vor allem fünf Kategorien auf: Locking-Probleme, Berechtigungen, Korruption, Schema-Abweichungen und verletzte Constraints. Auf dieser Seite gehen wir jede dieser häufigen SQLite-Fehler der Reihe nach durch — was sie auslöst, was sie tatsächlich bedeuten und wie du sie behebst.
Zu jeder Fehlermeldung gehört ein numerischer Code (die Extended Codes sind sogar noch präziser). In den Logs siehst du in der Regel beide Varianten:
Fehler: Datenbank ist gesperrt -- Code 5 (SQLITE_BUSY)
Fehler: Datenbank kann nicht geöffnet werden -- Code 14 (SQLITE_CANTOPEN)
Fehler: Versuch, in eine schreibgeschützte zu schreiben -- Code 8 (SQLITE_READONLY)
Fehler: Datenbank-Disk-Image ist -- Code 11 (SQLITE_CORRUPT)
Wer den Code kennt, sucht effizienter — nach SQLITE_BUSY findet man deutlich bessere Treffer als nach der bloßen englischen Fehlermeldung.
database is locked (SQLITE_BUSY)
Der Klassiker unter den SQLite-Fehlern, sobald eine Anwendung von mehreren Stellen aus schreibt. SQLite serialisiert Schreibzugriffe: Nur eine Verbindung darf den Write-Lock gleichzeitig halten. Schafft es ein zweiter Writer nicht, den Lock innerhalb des Busy-Timeouts zu bekommen, schlägt genau dieser Fehler zu.
Drei Lösungsansätze, sortiert nach Wirkung:
WAL Mode allein löst das Locking-Problem bei den meisten Workloads. Der Busy Timeout ist dein Sicherheitsnetz für echte Schreibkonflikte. Abgesehen von den Einstellungen solltest du deinen Code prüfen: Eine Transaktion, die offen bleibt, während das Programm Netzwerk-I/O macht, hält das Lock die ganze Zeit über. Halte Transaktionen kurz und mache COMMIT (oder ROLLBACK), sobald die Arbeit erledigt ist.
unable to open database file (SQLITE_CANTOPEN)
Bei diesem SQLite-Fehler hat SQLite versucht, die Datei zu öffnen, und das Betriebssystem hat abgelehnt. In 95 % der Fälle liegt das Problem am Dateipfad oder am übergeordneten Verzeichnis:
-- Zu prüfende Punkte:
-- 1. Existiert der Pfad? ls -l /pfad/zu/db.sqlite
-- 2. Existiert das übergeordnete Verzeichnis? SQLite legt die Datei an,
-- aber nicht das darüberliegende Verzeichnis.
-- 3. Hat der Benutzer, unter dem Ihr Prozess läuft, Lese- und Schreibrechte
-- auf das Verzeichnis (nicht nur auf die Datei)?
-- 4. Ist das Volume eingebunden, nicht voll und nicht schreibgeschützt?
Ein subtiler Fall: SQLite muss neben der Datenbank Hilfsdateien (-journal, -wal, -shm) anlegen. Ist die Datei selbst beschreibbar, das Verzeichnis aber nicht, klappt das Öffnen — die Schreibvorgänge schlagen jedoch fehl. Vergib deshalb immer auch Schreibrechte auf Verzeichnisebene.
attempt to write a readonly database (SQLITE_READONLY) — sqlite readonly database fix
Ein naher Verwandter des vorherigen Fehlers. Die Datei lässt sich problemlos öffnen, aber Schreibzugriffe scheitern. Die Ursachen, nach Häufigkeit sortiert:
- Der Benutzer des Betriebssystems hat keine Schreibrechte auf die Datei oder das Verzeichnis.
- Die Verbindung wurde mit einem Read-only-Flag geöffnet (
SQLITE_OPEN_READONLYodermode=roin einer URI). - Das Volume ist read-only eingehängt (typisch bei Docker-Bind-Mounts und manchen Cloud-Dateisystemen).
- Die Datenbank liegt auf einem Netzwerk-Dateisystem, das die von SQLite benötigten Locking-Mechanismen nicht unterstützt.
Korrigiere die Berechtigungen oder hänge das Volume neu ein. Bei Docker solltest du sicherstellen, dass der Bind-Mount nicht auf :ro steht und dass das Verzeichnis dem Container-User gehört.
database disk image is malformed (SQLITE_CORRUPT)
Die Bytes der Datei passen nicht mehr zum SQLite-Format. Die echten Ursachen liegen meistens in der Umgebung: abgebrochene Prozesse mitten im Schreibvorgang auf Dateisystemen ohne sauberes fsync, Kopieren der Datenbank während ein Writer noch aktiv war, Hardware-Defekte oder das Synchronisieren der Datei über Dropbox bzw. iCloud.
Prüfe zuerst, wie schlimm der Schaden tatsächlich ist:
Wenn integrity_check ein ok zurückliefert, ist deine Datenbank in Ordnung – der Fehler kommt dann von woanders her (häufig von einer veralteten Verbindung). Bekommst du dagegen eine Liste mit Problemen zurück, musst du die Daten retten.
Der sauberste Weg zur Wiederherstellung führt über den Befehl .recover der CLI. Er holt sich alles, was noch lesbar ist, und schreibt es in eine frische Datenbank:
sqlite3 corrupt.db ".recover" | sqlite3 recovered.db
sqlite3 recovered.db "PRAGMA integrity_check;"
Wenn du ein aktuelles Backup hast, spiel lieber das ein – das geht schneller und du musst dir nicht den Kopf darüber zerbrechen, ob du wirklich „fast alles" zurückbekommen hast. Wie man eine laufende Datenbank korrekt kopiert (Spoiler: nicht mit cp), steht auf der Seite zu Backup und Restore.
no such table und no such column
Diese Meldungen meinen genau das, was sie sagen – die Ursache ist aber meistens eine von zwei Sachen: Entweder bist du mit einer anderen Datenbank verbunden, als du denkst, oder eine Migration ist nicht durchgelaufen.
Prüfe den Connection-String deiner Anwendung — relative Pfade werden ausgehend vom aktuellen Arbeitsverzeichnis aufgelöst, und das unterscheidet sich zwischen Terminal, IDE und Produktivprozess. Eine In-Memory-Datenbank (:memory:) startet bei jedem Aufruf bei null, was viele auf dem falschen Fuß erwischt, wenn sie eigentlich mit persistenten Daten rechnen.
Auch das Quoting von Bezeichnern ist nicht zu unterschätzen. Ohne Anführungszeichen sind Namen case-insensitive, aber "User" und "user" gelten als zwei verschiedene Bezeichner. Wenn du eine Tabelle mit Anführungszeichen angelegt hast, musst du sie auch beim Zugriff weiterhin quoten.
Constraint-Verletzungen
SQLite verweigert Schreibvorgänge, die eine Constraint verletzen würden. Die Fehlermeldung sagt dir genau, welche:
Hinter jedem dieser Fehler steckt ein eigener Code (SQLITE_CONSTRAINT_UNIQUE, SQLITE_CONSTRAINT_CHECK, SQLITE_CONSTRAINT_NOTNULL). Die Lösung liegt fast immer auf der Anwendungsebene: Eingaben prüfen, bevor du schreibst, oder mit INSERT ... ON CONFLICT arbeiten, um Duplikate bewusst abzufangen.
FOREIGN KEY-Einschränkung fehlgeschlagen verdient einen eigenen Hinweis: Fremdschlüssel sind in SQLite standardmäßig deaktiviert. Schaltest du sie nicht aktiv ein, landen ungültige Verweise stillschweigend in der Datenbank — und fliegen dir erst um die Ohren, wenn du die Prüfung später aktivierst. Setze das Pragma deshalb bei jeder Verbindung:
cannot start a transaction within a transaction
Du hast BEGIN aufgerufen, obwohl bereits eine Transaktion offen war. Verschachtelte Transaktionen erlaubt SQLite nicht – verschachtelte Savepoints dagegen schon, und die liefern dir genau denselben Effekt:
Wenn dein ORM oder Framework Transaktionen verwaltet, hast du wahrscheinlich versehentlich zweimal eine gestartet. Prüfe, ob Autocommit aktiv ist und ob dein Connection Pool eine Verbindung wiederverwendet, auf der bereits eine Transaktion offen ist.
disk I/O error (SQLITE_IOERR)
Das Betriebssystem hat einen Lese- oder Schreibvorgang abgelehnt. Volle Platte, Schluckauf eines Netzwerkdateisystems, oder die Datei wurde SQLite einfach unter den Füßen weggezogen. Als Erstes solltest du df -h checken. Als Zweites, ob die Datenbank auf etwas Wackeligem wie NFS oder einem Cloud-synchronisierten Ordner liegt — SQLite setzt ein lokales POSIX-Dateisystem mit funktionierendem fsync voraus. Wenn du sie nicht umziehen kannst, musst du mit erhöhtem Korruptionsrisiko leben.
syntax error near "..."
Der Parser von SQLite verrät dir, welches Token ihn aus der Bahn geworfen hat. Die eigentliche Ursache liegt meist drei Zeilen vor der gemeldeten Stelle — ein fehlendes Komma, ein nicht gequoteter Bezeichner, der mit einem Schlüsselwort kollidiert, oder ein String mit einfachen Anführungszeichen, die escaped werden müssen ('it''s', nicht 'it's').
Nutze immer Parameter-Binding mit ?-Platzhaltern für Benutzereingaben, statt SQL per String-Verkettung zusammenzubauen – damit umgehst du nicht nur eine ganze Kategorie an Syntaxfehlern, sondern gleich auch noch SQL-Injection.
Checkliste zur Fehlerdiagnose
Wenn in der Produktion etwas schiefgeht, deckt diese Reihenfolge die meisten SQLite-Fehler in weniger als einer Minute ab:
Fünf Pragmas, fünf Antworten. Zusammen mit dem Fehlercode der fehlgeschlagenen Query weißt du sofort, in welche Schublade das Problem gehört und welche Doku-Seite du als Nächstes aufschlagen musst.
Abschluss des Tutorials
Das war die Tour. Von CREATE TABLE über Joins, Indizes, Transaktionen, WAL-Modus und Backups bis hin zu den Fehlerbildern, die auftauchen, sobald SQLite in der Praxis ankommt – du hast alles gesehen. Die Muster wiederholen sich: kurze Transaktionen, aktivierte Foreign Keys, WAL-Modus, regelmäßige Backups und ein gesunder Respekt vor PRAGMA integrity_check. Bleib bei diesen Gewohnheiten, und SQLite läuft jahrelang klaglos im Hintergrund.
Häufig gestellte Fragen
Warum meldet SQLite 'database is locked'?
Eine andere Verbindung hält gerade einen Schreib-Lock und dein Timeout ist abgelaufen. Drei Dinge helfen meistens: WAL-Modus aktivieren mit PRAGMA journal_mode=WAL, damit Leser die Schreiber nicht mehr blockieren, das Busy-Timeout hochsetzen via PRAGMA busy_timeout = 5000, und Transaktionen sauber committen, statt sie offen liegen zu lassen.
Wie behebe ich 'attempt to write a readonly database'?
Das ist fast immer ein Rechteproblem auf Dateisystemebene, kein SQLite-Bug. Der OS-User, unter dem dein Prozess läuft, braucht Schreibrechte sowohl auf die Datenbankdatei als auch auf das Verzeichnis drumherum — denn SQLite legt dort die Hilfsdateien -journal bzw. -wal an. Prüf also Owner, Mode-Bits und ob das Volume eventuell read-only gemountet ist.
Was bedeutet 'database disk image is malformed'?
SQLite hat Bytes gelesen, die nicht zum erwarteten Format passen — typische Ursachen sind abgewürgte Prozesse, defekte Platten oder das Kopieren der Datei während sie offen war. Mit PRAGMA integrity_check bestätigst du den Verdacht; danach kannst du in der CLI mit .recover retten, was noch zu retten ist, und in eine frische Datenbank dumpen. Ein Backup einzuspielen geht natürlich schneller, falls vorhanden.
Warum kommt 'no such table' oder 'no such column'?
Entweder bist du an einer anderen Datenbankdatei dran als gedacht, oder eine Migration ist nicht durchgelaufen. Mit PRAGMA database_list siehst du den Pfad, den SQLite tatsächlich geöffnet hat, und .schema tablename zeigt dir die echten Spalten. Auch Tippfehler und Groß-/Kleinschreibung sind häufige Stolperfallen — bei unquoteten Bezeichnern ist SQLite case-insensitive, bei quoteten dagegen nicht.