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
- Check server logs for the cleanup action's IMAP error — the JSON body usually carries it in d.error
- Refresh the folder so the grid reflects current UIDs, then re-run cleanup
- Ensure the account/folder were not changed while the modal was open
- 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
- Re-fetch UIDs immediately before bulk IMAP mutations — expunges invalidate cached ids
- Pass folder/account_id captured at list-load time consistently (as done here)
- Retry once on IMAP session drops before surfacing failure
- Apply partial successes server-side and report d.changed so users know what succeeded
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.