{"record":{"id":"6aba76652f09b325","repo":"odysseus-dev/odysseus","slug":"import-failed-6aba76","errorCode":null,"errorMessage":"Import failed","messagePattern":"Import failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/settings.js","lineNumber":4162,"sourceCode":"      if (btn) { btn.textContent = 'Importing…'; btn.disabled = true; }\n      try {\n        const texts = await Promise.all(files.map(f => f.text()));\n        const vcfParts = [];\n        const csvParts = [];\n        texts.forEach((text, idx) => {\n          const name = (files[idx]?.name || '').toLowerCase();\n          if (name.endsWith('.csv') || !String(text || '').toUpperCase().includes('BEGIN:VCARD')) csvParts.push(text);\n          else vcfParts.push(text);\n        });\n        let imported = 0, total = 0, failed = 0;\n        const _postImport = async (body) => {\n          const r = await fetch('/api/contacts/import', {\n            method: 'POST', credentials: 'same-origin',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify(body),\n          });\n          const d = await r.json();\n          if (d.error) throw new Error(d.error);\n          imported += Number(d.imported || 0);\n          total += Number(d.total || 0);\n          failed += Number(d.failed || 0);\n        };\n        if (vcfParts.length) await _postImport({ vcf: vcfParts.join('\\n') });\n        if (csvParts.length) await _postImport({ csv: csvParts.join('\\n') });\n        if (!vcfParts.length && !csvParts.length) throw new Error('No contact data found');\n        const msg = `Imported ${imported}/${total}` + (failed ? ` (${failed} failed)` : '');\n        uiModule.showToast ? uiModule.showToast(msg) : null;\n      } catch (err) {\n        uiModule.showError ? uiModule.showError(err?.message || 'Import failed') : alert(err?.message || 'Import failed');\n      } finally {\n        if (btn) { btn.textContent = orig; btn.disabled = false; }\n        e.target.value = '';\n        await _renderContactsManager();\n      }\n    });\n    await _renderContactsManager();","sourceCodeStart":4144,"sourceCodeEnd":4180,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/settings.js#L4144-L4180","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n          const d = await r.json();\n          if (d.error) throw new Error(d.error);\n\n// after\n          const d = await r.json().catch(() => null);\n          if (!r.ok || !d) throw new Error((d && d.error) || `Import failed (HTTP ${r.status})`);\n          if (d.error) throw new Error(d.error);","handlingStrategy":"try-catch","validationCode":"const files = Array.from(e.target.files || []);\nif (!files.length) return;\nconst texts = await Promise.all(files.map(f => f.text().catch(() => '')));\nif (!texts.some(t => t.trim())) throw new Error('No contact data found');","typeGuard":null,"tryCatchPattern":"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'); }","preventionTips":["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"],"tags":["contacts","import","fetch","parsing","settings"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}