odysseus-dev/odysseus · error · Error

Cleanup failed

Error message

Cleanup failed

What it means

Thrown when the bulk cleanup POST (delete/junk actions from the unsubscribe flow) answers with a body whose success flag is falsy. The fallback message 'Cleanup failed' appears only when the server sent no error field; the resulting toast shows err.message.

Source

Thrown at static/js/emailLibrary.js:1493

    return;
  }
  const restoreBusy = _setUnsubButtonBusy(btn, action === 'junk' ? 'Marking spam' : 'Deleting');
  try {
    const r = await fetch(emailApiUrl('/api/email/unsubscribe/cleanup', {
      account_id: state._libAccountId || undefined,
    }), {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        action,
        uids,
        folder: state._libFolder || 'INBOX',
        account_id: state._libAccountId || '',
      }),
    });
    const d = await r.json().catch(() => ({}));
    if (!d.success) throw new Error(d.error || 'Cleanup failed');
    const removed = new Set(uids.map(uid => String(uid)));
    state._libEmails = state._libEmails.filter(e => !removed.has(String(e.uid)));
    try { _libCacheWriteBack(); } catch (_) {}
    try { _renderGrid(); } catch (_) {}
    modal.querySelector('.email-unsub-followup')?.remove();
    showToast?.(action === 'junk'
      ? `Marked ${d.changed || 0} email${Number(d.changed || 0) === 1 ? '' : 's'} as spam`
      : `Deleted ${d.changed || 0} email${Number(d.changed || 0) === 1 ? '' : 's'}`);
  } catch (err) {
    console.error(err);
    showToast?.(err?.message || 'Cleanup failed');
  } finally {
    restoreBusy?.();
  }
}

function _showUnsubscribeCleanupPrompt(modal, candidates) {
  if (!modal || !Array.isArray(candidates) || !candidates.length) return;

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check server logs for the cleanup action's IMAP error — the JSON body usually carries it in d.error
  2. Refresh the folder so the grid reflects current UIDs, then re-run cleanup
  3. Ensure the account/folder were not changed while the modal was open
  4. Retry after reconnecting the account if the IMAP session dropped
Defensive patterns

Strategy: retry

Validate before calling

const uids = selected.map(c => c.uid).filter(u => Number.isInteger(u));
if (!uids.length) { showToast('Nothing selected'); return; }

Try / catch

try { const d = await r.json().catch(() => ({})); if (!d.success) throw new Error(d.error || 'Cleanup failed'); ... } catch (err) { showToast(err?.message || 'Cleanup failed'); }

Prevention

When it happens

Trigger: Marking junk / deleting the batches of unsubscribe-sender emails: IMAP operation fails server-side (folder read-only, UIDSET invalid after a concurrent expunge, connection dropped), or the folder/account_id in the payload do not match the mailbox state.

Common situations: Another client expunged messages so the cached UIDs no longer resolve; server-side IMAP session invalidated mid-batch; account switched in the UI while the cleanup modal was open so account_id points at the wrong mailbox.

Related errors


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