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
- Check the export request's status in the network tab
- Re-login and retry; confirm the backend is up
- If 500, look for a specific contact the serializer chokes on (check server logs)
- 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
- Include res.status in the thrown error
- Verify auth before export on long-lived tabs
- Keep the finally-based button restore (already correct)
- Handle blob creation failures separately from HTTP failures
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.