quickwit-oss/tantivy · error

Field reader cache lock poisoned. This should never happen.

Error message

Field reader cache lock poisoned. This should never happen.

What it means

This is the write side of the same inverted-index reader cache in SegmentReader: after a cache miss, the reader is inserted under a write lock whose poisoning triggers this panic. Like the read side, poisoning implies a thread panicked while holding the cache lock.

Source

Thrown at src/index/segment_reader.rs:284

                "Failed to open field {:?}'s positions in the composite file. Has the schema been \
                 modified?",
                field_entry.name()
            );
            DataCorruption::comment_only(error_msg)
        })?;

        let inv_idx_reader = Arc::new(InvertedIndexReader::new(
            TermDictionary::open(termdict_file)?,
            postings_file,
            positions_file,
            record_option,
        )?);

        // by releasing the lock in between, we may end up opening the inverting index
        // twice, but this is fine.
        self.inv_idx_reader_cache
            .write()
            .expect("Field reader cache lock poisoned. This should never happen.")
            .insert(field, Arc::clone(&inv_idx_reader));

        Ok(inv_idx_reader)
    }

    /// Returns the list of fields that have been indexed in the segment.
    /// The field list includes the field defined in the schema as well as the fields
    /// that have been indexed as a part of a JSON field.
    /// The returned field name is the full field name, including the name of the JSON field.
    ///
    /// The returned field names can be used in queries.
    ///
    /// Notice: If your data contains JSON fields this is **very expensive**, as it requires
    /// browsing through the inverted index term dictionary and the columnar field dictionary.
    ///
    /// Disclaimer: Some fields may not be listed here. For instance, if the schema contains a json
    /// field that is not indexed nor a fast field but is stored, it is possible for the field
    /// to not be listed.

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Fix the root panic that poisoned the lock (check earlier stack traces for InvertedIndexReader::open failures).
  2. Repair or re-index the affected segment.
  3. Recreate the SegmentReader/Searcher after the failure.
  4. Reduce concurrency-induced panics by validating index integrity before searching.

Example fix

// before
let inv = reader.inverted_index(field)?; // panics: cache lock poisoned
// after: guard against root cause first
let inv = if segment_has_valid_index_files(seg) { reader.inverted_index(field)? } else { return Err(corrupt_segment_err) };
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    segment_reader.inverted_index(field)
}));
if res.is_err() { searcher = rebuild_searcher(&reader)?; }

Prevention

When it happens

Trigger: First access to a field's inverted index on a segment triggers a write-lock insert while another thread has panicked holding that lock; reached via inverted_index(), scorer, phrase_scorer, fields_metadata, or get_match_term_infos.

Common situations: Concurrent searches where one thread panics inside InvertedIndexReader::open (corrupt store/idx files), then other threads panic here on cache insert.

Related errors


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