quickwit-oss/tantivy · error

The IndexWriter does not have any lock. This is a bug, pleas

Error message

The IndexWriter does not have any lock. This is a bug, please report.

What it means

IndexWriter holds a special directory lock handle that guarantees only one writer exists per index. rollback() takes this lock out of self to construct a replacement writer; if the field is already None the writer invariant is broken, so tantivy panics and asks for a bug report.

Source

Thrown at src/indexer/index_writer.rs:576

    ///
    /// This cancels all of the updates that
    /// happened after the last commit.
    /// After calling rollback, the index is in the same
    /// state as it was after the last commit.
    ///
    /// The opstamp at the last commit is returned.
    pub fn rollback(&mut self) -> crate::Result<Opstamp> {
        debug!("Rolling back to opstamp {}", self.committed_opstamp);
        // marks the segment updater as killed. From now on, all
        // segment updates will be ignored.
        self.segment_updater.kill();
        let document_receiver_res = self.operation_receiver();

        // take the directory lock to create a new index_writer.
        let directory_lock = self
            ._directory_lock
            .take()
            .expect("The IndexWriter does not have any lock. This is a bug, please report.");

        let new_index_writer =
            IndexWriter::new(self.index.clone(), self.options.clone(), directory_lock)?;

        // the current `self` is dropped right away because of this call.
        //
        // This will drop the document queue, and the thread
        // should terminate.
        *self = new_index_writer;

        // Drains the document receiver pipeline :
        // Workers don't need to index the pending documents.
        //
        // This will reach an end as the only document_sender
        // was dropped with the index_writer.
        if let Ok(document_receiver) = document_receiver_res {
            for _ in document_receiver {}
        }

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Do not call rollback() twice on the same IndexWriter; after a rollback, only use the newly returned writer.
  2. Restructure cleanup code so rollback runs exactly once (use an Option/flag or a single exit path).
  3. Never share the IndexWriter across threads performing rollback concurrently — rollback requires &mut self / exclusive access.
  4. If hit in normal single-rollback usage, report it as a tantivy bug with a backtrace.

Example fix

// before
writer.rollback()?;
// cleanup path
writer.rollback()?; // panics: lock already taken
// after
let mut writer = writer.rollback()?;
// cleanup only via the new writer; rollback not repeated
writer.commit()?;
Defensive patterns

Strategy: type-guard

Validate before calling

// track rollback usage in your own code
struct RollbackGuard { done: bool }
impl RollbackGuard {
    fn can_rollback(&self) -> bool { !self.done }
}

Type guard

fn can_rollback(w: &IndexWriter, already_rolled_back: &mut bool) -> bool {
    if *already_rolled_back { return false; }
    *already_rolled_back = true; // mark before calling
    true
}

Try / catch

null

Prevention

When it happens

Trigger: Calling rollback() (or commit that internally uses the lock path) more than once after the _directory_lock has already been taken — e.g. calling rollback twice, or using the writer after a rollback replaced it.

Common situations: Error-handling code that calls rollback in a cleanup path after an earlier rollback/commit already consumed the lock; using a moved/dropped writer reference; concurrent rollback from two threads.

Related errors


AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05). Data as JSON: /api/errors/8d291a489f2da284. Report an issue: GitHub.