odysseus-dev/odysseus · error · Error
Import failed
Error message
Import failed
What it means
Fallback message when a contacts import fails without a specific error. Files are read, split into vcf/csv parts by content sniffing, POSTed to /api/contacts/import, and any thrown error is shown as err?.message || 'Import failed'. Note the unguarded r.json() inside _postImport: a non-JSON response rejects before the d.error check, yielding a confusing SyntaxError message rather than 'Import failed'.
Source
Thrown at static/js/settings.js:4162
if (btn) { btn.textContent = 'Importing…'; btn.disabled = true; }
try {
const texts = await Promise.all(files.map(f => f.text()));
const vcfParts = [];
const csvParts = [];
texts.forEach((text, idx) => {
const name = (files[idx]?.name || '').toLowerCase();
if (name.endsWith('.csv') || !String(text || '').toUpperCase().includes('BEGIN:VCARD')) csvParts.push(text);
else vcfParts.push(text);
});
let imported = 0, total = 0, failed = 0;
const _postImport = async (body) => {
const r = await fetch('/api/contacts/import', {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const d = await r.json();
if (d.error) throw new Error(d.error);
imported += Number(d.imported || 0);
total += Number(d.total || 0);
failed += Number(d.failed || 0);
};
if (vcfParts.length) await _postImport({ vcf: vcfParts.join('\n') });
if (csvParts.length) await _postImport({ csv: csvParts.join('\n') });
if (!vcfParts.length && !csvParts.length) throw new Error('No contact data found');
const msg = `Imported ${imported}/${total}` + (failed ? ` (${failed} failed)` : '');
uiModule.showToast ? uiModule.showToast(msg) : null;
} catch (err) {
uiModule.showError ? uiModule.showError(err?.message || 'Import failed') : alert(err?.message || 'Import failed');
} finally {
if (btn) { btn.textContent = orig; btn.disabled = false; }
e.target.value = '';
await _renderContactsManager();
}
});
await _renderContactsManager();View on GitHub (pinned to f9235ebbf1)
Solutions
- Check the import POST response in devtools — d.error carries the server-side parse reason
- Clean the file: UTF-8, proper .vcf/.csv content, no stray rows
- Raise any proxy body-size limit for large imports
- Guard r.json() with .catch and check r.ok explicitly so failures produce clear messages
Example fix
// before
const d = await r.json();
if (d.error) throw new Error(d.error);
// after
const d = await r.json().catch(() => null);
if (!r.ok || !d) throw new Error((d && d.error) || `Import failed (HTTP ${r.status})`);
if (d.error) throw new Error(d.error); Defensive patterns
Strategy: try-catch
Validate before calling
const files = Array.from(e.target.files || []);
if (!files.length) return;
const texts = await Promise.all(files.map(f => f.text().catch(() => '')));
if (!texts.some(t => t.trim())) throw new Error('No contact data found'); Try / catch
try { const r = await fetch('/api/contacts/import', {...}); const d = await r.json().catch(() => null); if (!r.ok || !d) throw new Error((d && d.error) || `Import failed (HTTP ${r.status})`); if (d.error) throw new Error(d.error); ... } catch (err) { (uiModule.showError || alert)(err?.message || 'Import failed'); } Prevention
- Guard r.json() — a non-JSON error body currently throws a confusing SyntaxError
- Check r.ok before parsing the body
- Validate file encoding/content before upload
- Show per-file failure counts (d.failed) to pinpoint bad rows
When it happens
Trigger: POST /api/contacts/import returns an error body ({error: ...}) or a non-JSON body (HTML error page, 502 from a proxy) making r.json() throw; malformed vCard/CSV rows failing server-side parsing; oversized upload rejected; network failure mid-import.
Common situations: Importing a .csv that is actually Excel-exported with BOM/encoding issues; vCard version quirks (3.0 vs 4.0 folded lines); proxy body-size limits; expired auth; the err?.message fallback rarely fires because r.json() failures produce their own messages.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/6aba76652f09b325.
Report an issue: GitHub.