fix: false verify-move failure when a move empties the source mailbox#11
Merged
Conversation
…ilbox Proton Bridge (gluon) answers a UID-referencing SEARCH on an empty mailbox with 'NO no such message' instead of an empty result: SEARCH UID keys resolve against the mailbox snapshot, and resolution errors when the snapshot has no messages (gluon snapMsgList.resolveUID). Moving the last message out of a mailbox therefore made the post-move verification SEARCH error, and MoveVerified reported a hard failure for a fully successful move. Verification searches now tolerate that answer only when a criteria-less SEARCH proves the mailbox is empty — no UIDs can be present, which is the confirmation the verifier wanted. Real SEARCH failures on non-empty mailboxes still abort. The same guard covers SetFlagVerified, where targeting an empty mailbox now yields per-UID failure rows instead of a hard error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the regression reported while cleaning up labeling tests:
higgs move "Labels/Needs-Reply-Test" Trash --uid 1printederror[imap]: MOVE ...: verify move on ...: no such messageeven though the move fully succeeded.Root cause
Proton Bridge's IMAP server (gluon) answers a UID-referencing SEARCH on an empty mailbox with
NO no such messageinstead of an empty result:buildSearchOpUIDresolves the UID set viasnapMsgList.resolveUID, which errors whenever the snapshot holds zero messages (gluonsnapshot_messages.go). Nonexistent UIDs in a non-empty mailbox resolve fine and match nothing — which is why bulk archives from INBOX always verified cleanly and this only surfaced when moving the last message out of a mailbox: the empty source is the expected success state, and the verification SEARCH misread it as a hard failure. The inverse of the original silent-failure bug, as reported.Fix
Verification searches (
presentUIDs,wrongFlagState) now go through auidSearchhelper: on a SEARCH error it re-checks with a criteria-less SEARCH (which gluon answers normally on empty mailboxes) — if the mailbox is provably empty, no UIDs can be present, which is exactly the confirmation the verifier needs; otherwise the original error still surfaces. No error-string matching, and genuine SEARCH failures on non-empty mailboxes still abort (covered by a regression test).SetFlagVerifiedgets the same guard: flag ops targeting an empty mailbox now produce per-UID failure rows instead of a hard abort.TDD:
internal/imapwrite/gluon_quirk_test.goadds a backend wrapper reproducing gluon's exact behavior (UID-criteria SEARCH errors iff mailbox empty) — the three regression tests fail with the exact reported error message before the fix. Fullgo test ./... -racepasses.Also investigated:
extract --apply-as-labelvsclassify --applylabelingNot a code divergence: all three label paths (
classify --apply,apply-labels,extract --apply-as-label) call the identicalimapapply.ApplyLabels(UID COPY toLabels/<name>). The behavioral difference observed is Proton's server-side semantics of the destination: COPY to aLabels/mailbox adds a label view onto the single underlying message (so moving it from the label mailbox moves the one real message), while COPY to a folder (e.g. Archive) creates an independent copy, since a Proton message lives in exactly one folder. The earlier "genuine duplicates in Archive" came from folder copies (likely the since-fixed partial-MOVE fallback), not from labeling. So the single-message multi-view behavior is intentional, consistent across both paths, and is what makes labels work as persistent searchable tags.🤖 Generated with Claude Code