odysseus-dev/odysseus · error · Error

Export failed

Error message

Export failed

What it means

Error toast/alert when exporting the address book fails. _downloadContacts GETs /api/contacts/export?format=csv|vcf and throws 'Export failed' on any non-OK response; the same catch also covers blob/download/URL errors. The button is disabled and shows 'Exporting...' during the run and restored in finally.

Source

Thrown at static/js/settings.js:4116

      // name aren't useful as a contact.
      if (!name && !email) { (name ? el('cm-add-email') : el('cm-add-name')).focus(); return; }
      try {
        await fetch('/api/contacts/add', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, email, phone, address }) });
      } catch (_) {}
      el('cm-add-name').value = '';
      el('cm-add-email').value = '';
      if (el('cm-add-phone')) el('cm-add-phone').value = '';
      if (el('cm-add-address')) el('cm-add-address').value = '';
      el('cm-add-row').style.display = 'none';
      await _renderContactsManager();
    });
    const _downloadContacts = async (format) => {
      const btn = el(format === 'csv' ? 'cm-export-csv-btn' : 'cm-export-vcf-btn');
      const orig = btn ? btn.textContent : '';
      if (btn) { btn.textContent = 'Exporting...'; btn.disabled = true; }
      try {
        const res = await fetch(`/api/contacts/export?format=${encodeURIComponent(format)}`, { credentials: 'same-origin' });
        if (!res.ok) throw new Error('Export failed');
        const blob = await res.blob();
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = format === 'csv' ? 'odysseus-contacts.csv' : 'odysseus-contacts.vcf';
        document.body.appendChild(a);
        a.click();
        a.remove();
        setTimeout(() => URL.revokeObjectURL(url), 1000);
      } catch (_) {
        uiModule.showError ? uiModule.showError('Export failed') : alert('Export failed');
      } finally {
        if (btn) { btn.textContent = orig; btn.disabled = false; }
      }
    };
    el('cm-export-vcf-btn')?.addEventListener('click', () => _downloadContacts('vcf'));
    el('cm-export-csv-btn')?.addEventListener('click', () => _downloadContacts('csv'));

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check the export request's status in the network tab
  2. Re-login and retry; confirm the backend is up
  3. If 500, look for a specific contact the serializer chokes on (check server logs)
  4. Surface res.status in the thrown error instead of the fixed string

Example fix

// before
        if (!res.ok) throw new Error('Export failed');

// after
        if (!res.ok) throw new Error(`Export failed (HTTP ${res.status})`);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!['csv','vcf'].includes(format)) return;
if (!navigator.onLine) { uiModule.showError('Offline'); return; }

Try / catch

try { const res = await fetch(url, { credentials: 'same-origin' }); if (!res.ok) throw new Error(`Export failed (HTTP ${res.status})`); ... } catch (e) { (uiModule.showError || alert)(e.message); } finally { if (btn) { btn.textContent = orig; btn.disabled = false; } }

Prevention

When it happens

Trigger: GET /api/contacts/export returns 4xx/5xx (invalid format param, auth expired, server-side serialization failure of a malformed contact) or the fetch/blob rejects (offline, server down, oversized body).

Common situations: Expired auth cookie on a long-lived settings tab; backend restarted; a malformed contact row breaking the server-side CSV/vCard generation; proxy timeouts on large address books.

Related errors


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