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

  1. Check the backend response body (Network tab) — data.error/data.detail usually names the real cause (e.g. 'document not found').
  2. Verify the kind/id being sent actually exists by listing /api/documents/library or /api/gallery/library first.
  3. Confirm the session is authenticated (cookie present) since the request uses credentials: 'same-origin'.
  4. 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

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


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/c7bd38ea9369acec. Report an issue: GitHub.