Answer with an error when notmuch will not build an item master
`ThreadsIterator.next` and `MessagesIterator.next` asked libnotmuch for the item at the cursor and wrote `orelse unreachable`, reasoning that `notmuch_threads_valid` had just said there was one there. libnotmuch makes no such promise. The item does not exist until it is asked for -- it is built out of the database on the spot -- and the header says so plainly: Get the current thread from 'threads' as a notmuch_thread_t. ... If an out-of-memory situation occurs, this function will return NULL. So NULL is an outcome, and `unreachable` turned it into a panic that takes the whole program down. It is not a rare or theoretical one either: any archive read while it is written can produce it, because a writer committing underneath a running search invalidates the reader and the next item then fails to build. A daemon serving searches alongside deliveries died on it about once a minute. Both now return `error.XapianException`, which `NextError` gains -- and that is a breaking change to two public error sets, since an exhaustive `switch` over either no longer compiles. A caller that propagates with `try`, or switches with an `else`, is unaffected. Hence 0.3.0 rather than 0.2.1, and a CHANGELOG.md to say so. **Xapian exception rather than `OutOfMemory`**, which is what the header's wording would suggest, for two reasons. The allocator is rarely the real cause -- what is actually being reported is that the database could not answer -- and a caller is entitled to treat running out of memory as fatal, which would mean giving up on something it should have retried. `Directory` already maps a NULL return from libnotmuch this way, so this is the binding agreeing with itself. The string iterators are left alone deliberately. `notmuch_tags_get` and the others hand back a pointer into an object the caller already holds rather than building anything, so a NULL there really would mean the cursor was invalid -- which is checked immediately above. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>