odysseus-dev/odysseus · error · Error
data?.error || data?.detail || `HTTP ${res.status}`
Error message
data?.error || data?.detail || `HTTP ${res.status}` What it means
Thrown by _stageOdysseusAttachment in static/js/document.js when POST /api/email/compose-from-odysseus returns a non-2xx status or a JSON body without success=true. The message prefers the backend's error/detail fields and falls back to the bare HTTP status code. It means the backend refused or failed to stage an Odysseus item as an email-compose attachment token.
Source
Thrown at static/js/document.js:3256
if (kind === 'gallery') {
return item.caption || item.prompt || item.filename || 'Gallery image';
}
return item.title || 'Untitled document';
}
async function _stageOdysseusAttachment(kind, id) {
const doc = docs.get(activeDocId);
if (!doc || doc.language !== 'email') return null;
if (!doc._composeAtts) doc._composeAtts = [];
const res = await fetch(`${API_BASE}/api/email/compose-from-odysseus`, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ kind, id }),
});
let data = null;
try { data = await res.json(); } catch (_) {}
if (!res.ok || !data?.success) throw new Error(data?.error || data?.detail || `HTTP ${res.status}`);
doc._composeAtts.push({
token: data.token,
filename: data.filename,
size: data.size || 0,
});
return data;
}
async function _stageOdysseusZip(items) {
const doc = docs.get(activeDocId);
if (!doc || doc.language !== 'email') return null;
if (!doc._composeAtts) doc._composeAtts = [];
const res = await fetch(`${API_BASE}/api/email/compose-from-odysseus-zip`, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items }),
});View on GitHub (pinned to f9235ebbf1)
Solutions
- Check the backend response body (Network tab) — data.error/data.detail usually names the real cause (e.g. 'document not found').
- Verify the kind/id being sent actually exists by listing /api/documents/library or /api/gallery/library first.
- Confirm the session is authenticated (cookie present) since the request uses credentials: 'same-origin'.
- If behind a proxy, raise the timeout for this endpoint because zipping/staging large attachments can exceed defaults.
Example fix
// before
if (!res.ok || !data?.success) throw new Error(data?.error || data?.detail || `HTTP ${res.status}`);
// after — include which item failed so the toast is actionable
if (!res.ok || !data?.success) {
const reason = data?.error || data?.detail || `HTTP ${res.status}`;
throw new Error(`Failed to attach ${kind}/${id}: ${reason}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!kind || !id) return; // nothing to stage const doc = docs.get(activeDocId); if (!doc || doc.language !== 'email') return;
Type guard
function isStagedAttachment(data) {
return data != null && typeof data === 'object'
&& data.success === true
&& typeof data.token === 'string' && data.token.length > 0
&& typeof data.filename === 'string';
} Try / catch
try {
const data = await _stageOdysseusAttachment(kind, id);
if (!data) return;
} catch (err) {
console.error('Odysseus attach failed:', err);
if (uiModule) uiModule.showError(`Attachment failed: ${err.message}`);
} Prevention
- Verify the item still exists via the library listing before offering it in the attach menu.
- Keep frontend and backend deployed together so the compose-from-odysseus contract (token/filename/size) stays in sync.
- Handle auth expiry globally (401 interceptor / re-login prompt) instead of per call site.
When it happens
Trigger: POST /api/email/compose-from-odysseus with {kind, id} where the referenced Odysseus document/gallery item does not exist, the attachment file is unreadable server-side, the session cookie is missing (credentials: 'same-origin'), or a proxy returns 502/503 with a non-JSON body so data stays null and only `HTTP 502` surfaces.
Common situations: Clicking 'attach from Odysseus' in the email composer after the source document was deleted on the server; expired login session; reverse proxy (nginx) timing out on a large attachment; backend version that predates the endpoint returning 404.
Related errors
- (data && (data.error || data.detail)) || `HTTP ${res.status}
- Document create failed: HTTP ${res.status}
- Document save failed: HTTP ${res.status}
- HTTP ${res.status}
- statusText
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/c7bd38ea9369acec.
Report an issue: GitHub.