{"record":{"id":"5b4725956d4b11aa","repo":"quickwit-oss/tantivy","slug":"empty-docopstamp-is-forbidden","errorCode":null,"errorMessage":"Empty DocOpstamp is forbidden","messagePattern":"Empty DocOpstamp is forbidden","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/indexer/index_writer.rs","lineNumber":244,"sourceCode":"}\n\n/// `doc_opstamps` is required to be non-empty.\nfn apply_deletes(\n    segment: &Segment,\n    delete_cursor: &mut DeleteCursor,\n    doc_opstamps: &[Opstamp],\n) -> crate::Result<Option<BitSet>> {\n    if delete_cursor.get().is_none() {\n        // if there are no delete operation in the queue, no need\n        // to even open the segment.\n        return Ok(None);\n    }\n\n    let max_doc_opstamp: Opstamp = doc_opstamps\n        .iter()\n        .cloned()\n        .max()\n        .expect(\"Empty DocOpstamp is forbidden\");\n\n    let segment_reader = SegmentReader::open(segment)?;\n    let doc_to_opstamps = DocToOpstampMapping::WithMap(doc_opstamps);\n\n    let max_doc = segment.meta().max_doc();\n    let mut deleted_bitset = BitSet::with_max_value_and_full(max_doc);\n    let may_have_deletes = compute_deleted_bitset(\n        &mut deleted_bitset,\n        &segment_reader,\n        delete_cursor,\n        &doc_to_opstamps,\n        max_doc_opstamp,\n    )?;\n    Ok(if may_have_deletes {\n        Some(deleted_bitset)\n    } else {\n        None\n    })","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/indexer/index_writer.rs#L226-L262","documentation":"During merge or apply_deletes, the writer collects per-doc operation stamps and takes their max via iterator .max().expect(...). An empty doc_opstamps list has no max, so this panic fires; the code assumes every segment being processed has at least one document with stamps.","triggerScenarios":"Calling index_documents followed by delete application on a segment that yields an empty doc_opstamps vector — e.g. merging/deleting against an empty segment or a segment with zero matching docs in custom merge/segment-management code.","commonSituations":"Merging segments where one is empty; custom code manipulating doc_opstamps; indexing zero documents and then triggering delete application; edge cases after purging all documents.","solutions":["Ensure apply_deletes/index_documents are not invoked for empty segments — filter segments with max_doc()==0 before merging.","Guard your merge/delete pipeline: skip segments with no documents.","If hit through normal usage, report as a tantivy bug with a reproduction and upgrade to a patched version.","After deleting all docs, avoid triggering merges/delete application on the now-empty segment until new documents exist."],"exampleFix":"// before\nlet mut segments = reader.searchable_segments()?;\nwriter.merge(&segments.iter().map(|s| s.id()).collect::<Vec<_>>())?; // panics if a segment is empty\n// after\nlet segments: Vec<_> = reader.searchable_segments()?.into_iter().filter(|s| s.meta().max_doc() > 0).collect();\nif !segments.is_empty() { writer.merge(&segments.iter().map(|s| s.id()).collect::<Vec<_>>())?; }","handlingStrategy":"validation","validationCode":"// skip empty segments before merge/delete application\nlet segments: Vec<SegmentId> = reader.searchable_segments()?\n    .into_iter()\n    .filter(|s| s.meta().max_doc() > 0)\n    .map(|s| s.id())\n    .collect();\nif segments.is_empty() { return Ok(()); }","typeGuard":"fn mergeable(seg: &Segment) -> bool { seg.meta().max_doc() > 0 }","tryCatchPattern":"null","preventionTips":["Filter out empty segments (max_doc()==0) before merging","Avoid applying deletes to segments with zero documents","Avoid purging all documents and then immediately forcing merges","Report and reproduce if triggered by default merge policies"],"tags":["rust","panic","indexing","merge","deletes"],"backgroundTag":"empty-collection-unwrwap","analyzedSha":"b5d8deb80c26924e6b007a5b1a7630f35ca64de4","analyzedAt":"2026-09-05T13:20:51.521Z","contentChangedAt":"2026-09-05T13:20:51.521Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}