odysseus-dev/odysseus · error · Error

t || r.statusText

Error message

t || r.statusText

What it means

Error thrown when the direct PDF export GET /api/document/{id}/export-pdf returns non-OK. The response body text is used verbatim as the message (fallback: statusText). Because the endpoint returns a PDF blob on success, an error body is typically plain text or JSON from the backend (e.g. 'document has no PDF backing').

Source

Thrown at static/js/document.js:624

    const ae = document.activeElement;
    const el = (ae && (ae.tagName === 'INPUT' || ae.tagName === 'TEXTAREA')) ? ae : ta;
    if (!el) return;
    try {
      el.setAttribute('readonly', 'readonly');
      el.blur();
      setTimeout(() => { try { el.removeAttribute('readonly'); } catch (_) {} }, 120);
    } catch (_) { try { el.blur(); } catch (_) {} }
  }

  async function _downloadFilledPdf() {
    if (!activeDocId) return;
    _dismissDocKb();   // export shouldn't leave the keyboard up
    await _saveActiveDocBeforeExport();
    try {
      const r = await fetch(`${API_BASE}/api/document/${activeDocId}/export-pdf`);
      if (!r.ok) {
        const t = await r.text();
        throw new Error(t || r.statusText);
      }
      const blob = await r.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      const cd = r.headers.get('Content-Disposition') || '';
      const m = cd.match(/filename\*?=(?:UTF-8'')?"?([^"';]+)/i);
      const _slug = (s) => (s || 'form').replace(/\.pdf$/i, '').replace(/\s+/g, '_').replace(/[^A-Za-z0-9._-]/g, '').replace(/_+/g, '_').replace(/^_|_$/g, '') || 'form';
      a.download = (m && decodeURIComponent(m[1])) || (_slug(docs.get(activeDocId)?.title) + '_annotated.pdf');
      document.body.appendChild(a);
      a.click();
      a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    } catch (e) {
      if (uiModule) uiModule.showError('Export failed: ' + e.message);
      else alert('Export failed: ' + e.message);
    }
  }

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the thrown text — it is the endpoint's literal error body
  2. Confirm the document actually has a PDF backing (it must have been imported from a PDF)
  3. If the PDF is fillable-form-based, verify it opens in a viewer and is not password-protected
  4. Check backend logs for the PDF-generation exception

Example fix

// before
const t = await r.text();
throw new Error(t || r.statusText);

// after
const t = await r.text().catch(() => '');
let msg = t;
try { msg = JSON.parse(t).detail || t; } catch {}
throw new Error(msg || r.statusText || `HTTP ${r.status}`);
Defensive patterns

Strategy: try-catch

Validate before calling

const doc = docs.get(activeDocId);
if (!doc || !doc.hasPdf) { uiModule?.showToast?.('This document has no PDF to export'); return; }

Try / catch

try {
  const r = await fetch(`${API_BASE}/api/document/${activeDocId}/export-pdf`);
  if (!r.ok) {
    const t = await r.text().catch(() => '');
    let msg = t;
    try { msg = JSON.parse(t).detail || t; } catch {}
    throw new Error(msg || r.statusText || `HTTP ${r.status}`);
  }
  const blob = await r.blob();
  // ... trigger download
} catch (e) {
  uiModule?.showError?.('PDF export failed: ' + e.message);
}

Prevention

When it happens

Trigger: GET export-pdf returns 404 (unknown docId), 409/400 (document is markdown-only with no PDF to fill), 500 (pdf processing crash — malformed PDF, missing fillable fields), or a proxy HTML error page that then shows as raw HTML in the toast.

Common situations: Doc was created from text/markdown so no source PDF exists; the uploaded PDF is encrypted/corrupt; backend pdf library dependency missing; docId stale after a reload/deletion.

Related errors


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