odysseus-dev/odysseus · error · Error
HTTP ${res.status}
Error message
HTTP ${res.status} What it means
Thrown when POST /api/email/extract-style fails: the guard `!res.ok || !data.success || !data.style` covers HTTP failure, a success=false body, and a missing style payload, with the message coming from data.error or 'HTTP <status>'. The status element then shows the failure reason.
Source
Thrown at static/js/emailLibrary.js:415
wp = spinnerModule.createWhirlpool(14);
wp.element.style.cssText = 'display:inline-block;vertical-align:-3px;margin-right:6px;';
status.appendChild(wp.element);
status.appendChild(document.createTextNode('Analyzing sent emails…'));
} catch (_) {
status.textContent = 'Analyzing sent emails…';
}
}
try {
const res = await fetch(emailApiUrl('/api/email/extract-style', {
account_id: state._libAccountId || undefined,
}), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ sample_count: 15 }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok || !data.success || !data.style) throw new Error(data.error || `HTTP ${res.status}`);
if (styleEl) styleEl.value = data.style;
if (status) status.textContent = 'Extracted';
showToast?.('Email writing style extracted');
setTimeout(() => { if (status) status.textContent = ''; }, 1800);
} catch (err) {
if (status) status.textContent = err?.message || 'Extract failed';
showToast?.('Failed to extract email writing style');
} finally {
if (wp && wp.destroy) { try { wp.destroy(); } catch (_) {} }
extractBtn.disabled = false;
if (saveBtn) saveBtn.disabled = false;
}
});
}
function _hideEmailSettingsPage() {
const modal = document.getElementById('email-lib-modal');
const page = document.getElementById('email-lib-settings-page');View on GitHub (pinned to f9235ebbf1)
Solutions
- Read the status text next to the button — it carries data.error when present
- Ensure the selected account has sent-folder messages to sample (sample_count: 15 needs history)
- Re-authenticate the email account if IMAP/token errors are indicated
- Retry after rate limits clear; verify account_id matches a configured account
Defensive patterns
Strategy: try-catch
Try / catch
try { const data = await res.json().catch(() => ({})); if (!res.ok || !data.success || !data.style) throw new Error(data.error || `HTTP ${res.status}`); ... } catch (err) { status.textContent = err?.message || 'Extract failed'; } finally { btn.disabled = false; } Prevention
- Require a minimum sent-message count before enabling Extract
- Treat success+style as one combined guard (as here) — partial success still fails safely
- Reset button/spinner state in finally so failures never lock the UI
When it happens
Trigger: Clicking Extract in the email style settings: server cannot fetch enough sample messages (empty sent folder → no style), the account_id in state is stale, IMAP authentication fails mid-extraction, or the LLM call used to summarize style errors out.
Common situations: New account with too few sent messages; OAuth token expired between list and extract; provider rate limit on the style-summarization model; JSON parse failure handled as {} so data.error is undefined and the bare status shows.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/4cf2f951a672eeff.
Report an issue: GitHub.