odysseus-dev/odysseus · error · Error

PDF import failed: ${_e}

Error message

PDF import failed: ${_e}

What it means

Thrown in documentLibrary.js's batch importer when POST /api/documents/import-pdf for a dropped PDF returns non-2xx. This is the better-diagnosed sibling of error 51: it extracts _j.detail/_j.error and prefixes 'PDF import failed: ', so the surfaced message in the end-of-import summary ('Imported N files, M failed — <first error>') carries the backend reason.

Source

Thrown at static/js/documentLibrary.js:1525

        const language = EXT_TO_LANG[ext] !== undefined ? EXT_TO_LANG[ext] : null;

        const isSpreadsheet = ['.xlsx', '.xls', '.ods'].includes(ext);
        const isPdf = ext === '.pdf';

        if (isPdf) {
          // Backend handles save + AcroForm detection in one shot — picks the
          // right doc kind so fillable forms get clickable inputs in the PDF
          // view, and plain PDFs get the static page-image viewer.
          const fd = new FormData();
          fd.append('file', file);
          const res = await fetch(`${API_BASE}/api/documents/import-pdf`, {
            method: 'POST',
            body: fd,
          });
          if (!res.ok) {
            let _e = `HTTP ${res.status}`;
            try { const _j = await res.json(); _e = _j.detail || _j.error || _e; } catch {}
            throw new Error('PDF import failed: ' + _e);
          }
          imported++;
          continue;
        }

        if (isSpreadsheet) {
          // Multi-sheet: create one document per sheet
          await ensureXLSX();
          const buf = await file.arrayBuffer();
          const wb = window.XLSX.read(buf, { type: 'array' });
          for (const sheetName of wb.SheetNames) {
            const csv = window.XLSX.utils.sheet_to_csv(wb.Sheets[sheetName]);
            if (!csv.trim()) continue;
            const sheetTitle = wb.SheetNames.length > 1
              ? `${baseTitle} - ${sheetName}` : baseTitle;
            const res = await fetch(`${API_BASE}/api/document`, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the _firstErr suffix of the summary toast — it is the backend's detail for the first failing file.
  2. Import the failing file alone to confirm which one it is.
  3. Remove encryption/repair the PDF (qpdf --decrypt) and retry.
  4. Raise upload size/time limits if large scans are legitimate.
Defensive patterns

Strategy: try-catch

Validate before calling

if (file.type !== 'application/pdf' && !file.name.toLowerCase().endsWith('.pdf')) continue;
if (file.size > PDF_MAX_BYTES) { failed++; _firstErr ||= `${file.name}: too large`; continue; }

Try / catch

try {
  const res = await fetch(`${API_BASE}/api/documents/import-pdf`, { method: 'POST', body: fd, credentials: 'same-origin' });
  if (!res.ok) {
    let _e = `HTTP ${res.status}`;
    try { const _j = await res.json(); _e = _j.detail || _j.error || _e; } catch {}
    throw new Error(`PDF import failed (${file.name}): ` + _e);
  }
  imported++;
} catch (e) {
  failed++;
  if (!_firstErr) _firstErr = (e && e.message) || String(e);
}

Prevention

When it happens

Trigger: Batch-importing PDFs where one is encrypted/corrupt (422 with detail), exceeds the multipart size cap (413), or the backend PDF stack errors (500). Only the first failure's message survives into the summary (_firstErr), so later distinct causes are hidden.

Common situations: Mixing valid and password-protected PDFs in one drop; office scanners producing non-standard PDFs; proxy body limits on multi-MB scans.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/b6efddfb72340574. Report an issue: GitHub.