Paperless: Was tun wenn Dokumenten Export versagt?

Warum ist der Export wichtig? Paperless läuft doch!

Ja das stimmt, aber den Export brauchst Du

  • Evt. wenn Du von Paperless weg möchtest und die Dokumente mitnehmen
  • Evt. als individuelles Backup
  • Evt. beim Wechsel in Paperless zu anderen Datenbanken
  • Evt. beim Major Versions Upgrade von Paperless selbst

Meine Ausgangssituation:

  • Anfangs hatte ich von Docker und Bash Null Ahnung, jetzt zumindest kein blutiger Anfänger mehr :slight_smile:
  • Paperless Docker auf Synology NAS mit Container Manager installiert
  • 7200 Dokumente enthalten (größtenteils gescannt aber auch frühere PDF mit tlw langen Dateinamen)
  • Backup: In compose.yaml fast nur feste Versionen verdrahtet und mit Hyperbackup Versionsrotation. Zusätzlich über Aufgabenmanager/Bash Script ein regelmäßiger Postgres DB Dump)

Wichtig: Bevor Ihr etwas ausprobiert, immer ein Backup machen!

Hier das Script, mit dem ich den Dump und/oder den Dokumenten Export anstoße.

Zusammenfassung
#!/bin/bash

# === Konfiguration ===
DOKUMENTENEXPORT="2" # 1 = true
PROJECT_DIR="/volume1/docker/paperless-office"
DOKUMENTE_EXPORT_DIR="$PROJECT_DIR/export"

DB_CONTAINER="paperless-office-postgres"
DB_USER="xxx"
DB_NAME="xxx"
DB_VOLUME_PATH="/var/lib/postgresql/data"
DB_DUMP_FILENAME="backup_dbdump.sql"
DB_DUMP_PATH="$DB_VOLUME_PATH/$DB_DUMP_FILENAME"

# === Export-Verzeichnis prüfen und leeren ===
if [ -n "$DOKUMENTE_EXPORT_DIR" ] && [ -d "$DOKUMENTE_EXPORT_DIR" ]; then
  echo "[INFO] Leere Dokumenten-Export-Verzeichnis: $DOKUMENTE_EXPORT_DIR"
  rm -rf "$DOKUMENTE_EXPORT_DIR"/*
else
  echo "[ERROR] Dokumenten-Export-Verzeichnis existiert nicht. Abbruch."
  exit 1
fi

# === In Projektverzeichnis wechseln ===
cd "$PROJECT_DIR" || { echo "[ERROR] Projektverzeichnis nicht gefunden: $PROJECT_DIR"; exit 1; }


# === PostgreSQL Dump erstellen ===
# Erst der DB Dump weil wenn zu lange Namen, bricht das Script ab ohne den DB Dump zu machen
echo "[INFO] Erstelle PostgreSQL-Dump: $DB_DUMP_PATH"
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" -F p -f "$DB_DUMP_PATH"

if [ $? -eq 0 ]; then
  echo "[OK] Datenbank-Dump erfolgreich gespeichert unter: $PROJECT_DIR/postgres/$DB_DUMP_FILENAME"
else
  echo "[ERROR] Fehler beim Datenbank-Dump!"
  exit 1
fi

# === Dokumente exportieren ===
if [ "$DOKUMENTENEXPORT" = "1" ]; then
  echo "[INFO] Starte Dokumenten-Export..."
  if docker-compose exec -T webserver document_exporter ../export; then
    echo "[OK] Dokumenten-Export erfolgreich abgeschlossen."
  else
    echo "[WARNUNG] Dokumenten-Export fehlgeschlagen – wird ignoriert."
    echo "[WARN] Fehler beim Export am $(date)" >> "$PROJECT_DIR/backup.log"
  fi
fi

Welche Probleme traten beim Export der Dokumente auf?

  • Script brach brach Mitten im Export ab mit Exception ab “OSError: [Errno 36] File name too long:”
Zusammenfassung
Traceback (most recent call last):
  File "/usr/src/paperless/src/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 413, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 233, in handle
    self.dump()
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 337, in dump
    self.copy_document_files(
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 510, in copy_document_files
    self.check_and_copy(
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 574, in check_and_copy
    if target.exists():
       ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/pathlib.py", line 860, in exists
    self.stat(follow_symlinks=follow_symlinks)
  File "/usr/local/lib/python3.12/pathlib.py", line 840, in stat
    return os.stat(self, follow_symlinks=follow_symlinks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 36] File name too long: '/usr/src/paperless/export/2011-04-11 xxx ganz langer Name.pdf'
[WARNUNG] Dokumenten-Export fehlgeschlagen – wird ignoriert.
  • Script brach Mitten im Export ab mit Exception ab “FileNotFoundError: [Errno 2] No such file or directory:”
Zusammenfassung
Traceback (most recent call last):
  File "/usr/src/paperless/src/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 413, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 233, in handle
    self.dump()
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 337, in dump
    self.copy_document_files(
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 522, in copy_document_files
    self.check_and_copy(
  File "/usr/src/paperless/src/documents/management/commands/document_exporter.py", line 591, in check_and_copy
    copy_file_with_basic_stats(source, target)
  File "/usr/src/paperless/src/documents/utils.py", line 46, in copy_file_with_basic_stats
    shutil.copy(source, dest)
  File "/usr/local/lib/python3.12/shutil.py", line 435, in copy
    copyfile(src, dst, follow_symlinks=follow_symlinks)
  File "/usr/local/lib/python3.12/shutil.py", line 260, in copyfile
    with open(src, 'rb') as fsrc:
         ^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/usr/src/paperless/media/documents/archive/documents/originals/0000823.pdf'
[WARNUNG] Dokumenten-Export fehlgeschlagen – wird ignoriert.

Noch eins vorweg, es gibt sicher auch noch andere Möglichkeiten, den Problemen beizukommen. Aber so habe ich es gestern bis zu einem wieder funktionierendem Export hinbekommen.

Lösung für zu lange Namen:

  • Verbinde Dich per SSH als admin und sudo -i (Vorsicht ab diesem Punkt!)

  • docker ps zeigt Dir die Namen

  • Verbinde Dich zur Datenbank: docker exec -it paperless-office-postgres psql -U DEINUSER -d DEINEPAPERLESSDB

  • Wenn Du das siehst, bist Du auf der DB
    image

  • Kopiere das SQL in Deine SSH Session

SELECT id, title, LENGTH(title) AS title_length
FROM documents_document
WHERE LENGTH(title) > 100
ORDER BY title_length ASC;

Jetzt habe ich es mit Try-And-Error versucht und kam bei mir zu dem Schluß die Dateinamen dürfen maximal 130 Zeichen sein, damit sie mit Pfaden nachher nicht die 255 erlaubte Zeichen überschreiten und so zu obiger Exception beim Export führen. Damit ich hier etwas zeigen kann, habe ich das SQL Statement auf 100 Zeichen gesetzt. Du kannst die Zahl auch heruntersetzen, um bei Dir etwas zu sehen. Das ist ein lesender Befehl, der nichts kaputt macht.

  • Danach bin ich in Paperless gegangen und habe die Dateinamen verkürzt.

Danach gab es diese Exception beim Export nicht mehr.

Lösung von verwaisten Referenzen ohne reale Dokumente

Meine Vermutung wie es dazu kommen konnte:
Beim Einscannen hatte ich ein paar gemerkt, daß Seiten zu einem Dokument gehören, ich aber den ersten Teil bereits eingescannt und nach CONSUME geschickt hatte. Da Paperless mit dem Import anderer beschäftigt war, habe ich das letzte gelöscht und dachte es geht. Aber vermutlich referenziert Paperless bereits jedes Dokument im CONSUME. Wie gesagt, ist nur eine Vermutung.

  • Falls Ihr noch in voriger DB Session seid, müßt Ihr vorher mit \q wieder raus
  • Ansonsten wie oben beschrieben, SSH auf Docker
  • Gebe diesen Befehl ein um Python Script verwenden zu können
docker exec -it paperless-office-webserver python3 /usr/src/paperless/src/manage.py shell

image

  • Dann kopiert Ihr diese Befehle als eines in die Konsole
import os
from documents.models import Document

missing = []

for doc in Document.objects.all():
    try:
        abs_path = str(doc.archive_path)
        if not os.path.exists(abs_path):
            missing.append((doc.id, doc.title, abs_path))
    except Exception as e:
        missing.append((doc.id, doc.title, f"Fehler: {e}"))

Und mit

len(missing)

fragt Ihr das Array ab.
Gestern waren es bei mir 9, heute 0

  • Dann die “Dokumente” listen mit
for idx, (doc_id, title, path) in enumerate(missing, start=1): print(f"{idx}. ID: {doc_id}, Titel: {title}, Pfad/Fehler: {path}")

  • Bis hierhin war alles nur lesen, jetzt kommt ein Datenbank Inhalt schreibender Befehl, also bitte Vorischt! Ihr holt Euch die ID’s der Dokumente aus Eurem Konsolen Ergebnis und setzt den Befehl zusammen
from documents.models import Document; Document.objects.filter(id__in=[816, 584, 6697, 7053, 823, 335, 2417, 1328, 615]).delete()

Es gibt ein Fehlermeldungen, deren Ursache bereits bekannt ist. Das Dokument gibt es nicht.

  • Aus Python mit STRG+D herausgehen und mit EXIT SSH

Finaler Beweis eines wieder funktionierenden Exports

Obiges Backup Script angestoßen und bei > 7000 Dokumenten dauert es ein paar Minuten

Die verkürzte Log Ausgabe sieht so aus:

[INFO] Leere Dokumenten-Export-Verzeichnis: /volume1/docker/paperless-office/export
/bin/bash: line 17: /bin/rm: Argument list too long
[INFO] Erstelle PostgreSQL-Dump: /var/lib/postgresql/data/backup_dbdump.sql
[OK] Datenbank-Dump erfolgreich gespeichert unter: /volume1/docker/paperless-office/postgres/backup_dbdump.sql
[INFO] Starte Dokumenten-Export...

  0%|          | 0/7252 [00:00<?, ?it/s]
  0%|          | 4/7252 [00:00<03:28, 34.73it/s]
  0%|          | 14/7252 [00:00<01:45, 68.51it/s]
  0%|          | 24/7252 [00:00<01:28, 82.05it/s]
  1%|          | 48/7252 [00:00<00:50, 141.51it/s]
  1%|          | 67/7252 [00:00<00:45, 156.99it/s]
  1%|          | 83/7252 [00:00<00:45, 156.38it/s]
  1%|▏         | 104/7252 [00:00<00:41, 170.88it/s]
  2%|▏         | 122/7252 [00:00<00:42, 168.87it/s]
  2%|▏         | 146/7252 [00:00<00:40, 174.89it/s]
  2%|▏         | 172/7252 [00:01<00:35, 198.88it/s]
  3%|▎         | 193/7252 [00:01<00:36, 195.10it/s]
...
 90%|████████▉ | 6522/7252 [00:24<00:01, 485.20it/s]
 91%|█████████ | 6580/7252 [00:24<00:01, 508.83it/s]
 92%|█████████▏| 6637/7252 [00:24<00:01, 526.01it/s]
 92%|█████████▏| 6695/7252 [00:24<00:01, 540.96it/s]
 93%|█████████▎| 6753/7252 [00:24<00:00, 551.32it/s]
 94%|█████████▍| 6811/7252 [00:24<00:00, 557.91it/s]
 95%|█████████▍| 6869/7252 [00:25<00:00, 563.45it/s]
 96%|█████████▌| 6927/7252 [00:25<00:00, 568.03it/s]
 96%|█████████▋| 6985/7252 [00:25<00:00, 565.87it/s]
 97%|█████████▋| 7042/7252 [00:25<00:00, 548.49it/s]
 98%|█████████▊| 7100/7252 [00:25<00:00, 555.19it/s]
 99%|█████████▊| 7158/7252 [00:25<00:00, 559.77it/s]
 99%|█████████▉| 7215/7252 [00:26<00:00, 180.49it/s]
100%|██████████| 7252/7252 [00:28<00:00, 256.72it/s]
[OK] Dokumenten-Export erfolgreich abgeschlossen.

Vola, geht wieder, puuhh.

3 „Gefällt mir“