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
- Read the _firstErr suffix of the summary toast — it is the backend's detail for the first failing file.
- Import the failing file alone to confirm which one it is.
- Remove encryption/repair the PDF (qpdf --decrypt) and retry.
- 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
- Include the filename in per-file errors so multi-file failures are attributable (only _firstErr reaches the summary).
- Pre-check size and encryption where possible before the POST.
- Add credentials to the multipart request for auth-required deployments.
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
- PDF import failed
- (data && (data.error || data.detail)) || `HTTP ${res.status}
- No image provided
- No image
- data?.error || data?.detail || `HTTP ${res.status}`
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/b6efddfb72340574.
Report an issue: GitHub.