{"record":{"id":"9ca0c63ff1304b4c","repo":"quickwit-oss/quickwit","slug":"doc-batch-should-not-be-empty","errorCode":null,"errorMessage":"`doc_batch` should not be empty","messagePattern":"`doc_batch` should not be empty","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-ingest/src/ingest_v2/mrecordlog_utils.rs","lineNumber":90,"sourceCode":"            .await\n    } else {\n        let encoded_mrecords = doc_batch\n            .into_docs()\n            .map(|(_doc_uid, doc)| MRecord::Doc(doc).encode());\n\n        #[cfg(feature = \"failpoints\")]\n        fail_point!(\"ingester:append_records\", |_| {\n            let io_error = io::Error::from(io::ErrorKind::PermissionDenied);\n            Err(AppendDocBatchError::Io(io_error))\n        });\n\n        mrecordlog\n            .append_records(queue_id, None, encoded_mrecords)\n            .await\n    };\n    match append_result {\n        Ok(Some(offset)) => Ok(Position::offset(offset)),\n        Ok(None) => panic!(\"`doc_batch` should not be empty\"),\n        Err(AppendError::IoError(io_error)) => Err(AppendDocBatchError::Io(io_error)),\n        Err(AppendError::MissingQueue(queue_id)) => {\n            Err(AppendDocBatchError::QueueNotFound(queue_id))\n        }\n        Err(AppendError::Past) => {\n            panic!(\"`append_records` should be called with `position_opt: None`\")\n        }\n    }\n}\n\n/// Error returned when the mrecordlog does not have enough capacity to store some records.\n#[derive(Debug, Clone, Copy, thiserror::Error)]\npub(super) enum NotEnoughCapacityError {\n    #[error(\n        \"write-ahead log is full, capacity: {capacity}, usage: {usage}, requested: {requested}\"\n    )]\n    Disk {\n        usage: ByteSize,","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-ingest/src/ingest_v2/mrecordlog_utils.rs#L72-L108","documentation":"to_simple_list is a serde serializer helper for `Option<Vec<T>>` fields. It calls `.as_ref().expect(...)` on the Option, so serializing a field whose value is None panics. The helper assumes callers only ever attach it to fields already guaranteed to be Some (e.g. via `#[serde(serialize_with = \"to_simple_list\")]` on an always-populated field).","triggerScenarios":"Serializing (e.g. via serde_json) a struct whose field uses `serialize_with = \"to_simple_list\"` while that field holds None — typically constructing the struct with None for a list field that the helper was designed for non-None values.","commonSituations":"Deserializing a config/metadata file where the list field is absent or null and then re-serializing it; programmatically building index metadata/structs with None where a list is required; older metadata files written by a previous version lacking the field.","solutions":["Ensure the field is Some before serialization — populate the list explicitly when constructing the struct.","Change the field type to Vec<T> with a default (`#[serde(default)]`) so it can never be None.","Add a custom serializer that renders None as an empty string instead of panicking.","Fix the source data (config/index metadata file) so the field is present and non-null."],"exampleFix":"// before\nlet vec = &value\n    .as_ref()\n    .expect(\"attempt to serialize Option::None value\");\n// after\nlet Some(vec) = value.as_ref() else {\n    serializer.serialize_str(\"\")?; // or return serde::private::de::missing_field error\n    return Ok(());\n};","handlingStrategy":"type-guard","validationCode":"// in Rust, before serializing a struct using to_simple_list fields:\nassert!(some_struct.retention_period_opt.is_some(), \"list field must be Some before serialization\");","typeGuard":"fn ensure_some_list<T>(opt: &Option<Vec<T>>) -> Option<&Vec<T>> { opt.as_ref() }","tryCatchPattern":"// guard deserialized structs before re-serialization\nif struct_with_optional_list.list_field.is_none() {\n    return Err(anyhow!(\"missing list field in metadata; cannot serialize\"));\n}","preventionTips":["Prefer `#[serde(default)] Vec<T>` over Option<Vec<T>> for fields using this serializer.","When loading old metadata files, backfill missing list fields before re-serializing.","Add round-trip serialization tests for structs using to_simple_list.","Never construct such structs with None for the annotated fields."],"tags":["rust","serde","serialization","null-value","panic"],"backgroundTag":"null-argument","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}