odysseus-dev/odysseus · warning · Error
No contact data found
Error message
No contact data found
What it means
Thrown after reading the selected import files when neither vcfParts nor csvParts has content — the content-sniffing logic (extension .csv OR absence of 'BEGIN:VCARD') classified every file into neither bucket, which cannot normally happen, so in practice this fires when the file list itself is empty or reads returned empty text.
Solutions
- Confirm the selected file is non-empty and is a real .vcf/.csv
- Re-pick the file and retry the import
- Validate file size > 0 before reading, and early-return when no files are selected
- Move the no-data check before the POSTs (it already is) and warn earlier, at file-selection time
Example fix
// before
if (!vcfParts.length && !csvParts.length) throw new Error('No contact data found');
// after
if (!e.target.files || !e.target.files.length) return; // nothing selected
if (!vcfParts.length && !csvParts.length) throw new Error('No contact data found — file appears empty'); Defensive patterns
Strategy: validation
Validate before calling
const files = Array.from(e.target.files || []);
if (!files.length) return;
for (const f of files) {
if (f.size === 0) { uiModule.showError(`"${f.name}" is empty`); return; }
}
const looksVcf = t => String(t || '').toUpperCase().includes('BEGIN:VCARD');
const looksCsv = (name, t) => name.endsWith('.csv') || !looksVcf(t); Type guard
function hasContactData(text) { return /BEGIN:VCARD/i.test(text) || /.+,.+/s.test(text); } Try / catch
try { ... if (!vcfParts.length && !csvParts.length) throw new Error('No contact data found — file appears empty'); } catch (err) { (uiModule.showError || alert)(err?.message || 'Import failed'); } Prevention
- Reject 0-byte files at selection time
- Early-return when no files are chosen
- Confirm the extension matches the content (.csv vs .vcf)
- Keep the empty check before any POST fires (already the case)
When it happens
Trigger: File input change fired with no files selected; FileReader resolved with empty text (0-byte file); or the sniffing conditions are bypassed by an unexpected state (name without .csv and text containing 'BEGIN:VCARD' still goes to vcfParts, so the guard is near-unreachable).
Common situations: Selecting an empty (0-byte) file; programmatic clearing of the input then dispatching change; edge-case browser behavior where reader results are empty strings.
Related errors
- Expected a JSON object
- Import failed
- Invalid JSON
- Action name is required for action tasks
- Album name required
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/1cc33b47012b98c7.
Report an issue: GitHub.
Appendix: source
Thrown at static/js/settings.js:4169
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();
}
// Render the contacts list inside the manager card with inline edit +
// delete. Each row: name + emails; pencil flips to editable inputs.
async function _renderContactsManager() {
const list = el('cm-list');
if (!list) return;View on GitHub (pinned to f9235ebbf1)