{"record":{"id":"d3e331db04c93a87","repo":"t8y2/dbx","slug":"index-info-is-loaded-for-document-writes","errorCode":null,"errorMessage":"index info is loaded for document writes","messagePattern":"index info is loaded for document writes","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbx-core/src/db/meilisearch_driver.rs","lineNumber":594,"sourceCode":"#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct MeilisearchDocumentUpdate {\n    pub id: String,\n    pub doc_json: String,\n}\n\npub async fn save_document_batch(\n    client: &MeilisearchClient,\n    index: &str,\n    updates: &[MeilisearchDocumentUpdate],\n    delete_ids: &[String],\n    inserts: &[String],\n) -> Result<u64, String> {\n    let index_info =\n        if updates.is_empty() && inserts.is_empty() { None } else { Some(index_info(client, index).await?) };\n\n    if !updates.is_empty() {\n        let index_info = index_info.as_ref().expect(\"index info is loaded for document writes\");\n        let primary_key = index_info\n            .primary_key\n            .as_deref()\n            .ok_or_else(|| format!(\"Meilisearch index '{}' has no primary key\", index_info.uid))?;\n        let documents = updates\n            .iter()\n            .map(|update| {\n                let mut document = parse_document_object(&update.doc_json)?;\n                document.remove(\"_id\");\n                document.insert(primary_key.to_string(), decoded_identity(&update.id));\n                Ok(Value::Object(document))\n            })\n            .collect::<Result<Vec<_>, String>>()?;\n        submit_documents(client, index, Method::POST, documents).await?;\n    }\n\n    if !delete_ids.is_empty() {\n        let ids = delete_ids.iter().map(|id| decoded_identity(id)).collect::<Vec<_>>();","sourceCodeStart":576,"sourceCodeEnd":612,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-core/src/db/meilisearch_driver.rs#L576-L612","documentation":"Panic from `Option::expect` on the cached `index_info` in save_document_batch. The code loads index metadata whenever there are updates or inserts, then asserts it is Some inside the updates branch — which only runs when updates is non-empty, so the load already happened. A panic indicates the load condition and the branch condition drifted apart (a refactor bug), or index_info(client, index) returned but the Some(...) wrapper was bypassed; note that an actual metadata fetch failure already propagates via `?` above.","triggerScenarios":"Executing save_document_batch with non-empty updates where index_info ended up None — only possible if the earlier `if updates.is_empty() && inserts.is_empty()` load guard is changed without updating this branch, or index_info is replaced by a fallible source returning Option differently.","commonSituations":"Refactors altering the conditional metadata load; restructuring the batch method and dropping the Some(index_info(...)) binding; not reachable from Meilisearch server state (missing primary key has its own explicit error below).","solutions":["Keep the load guard and the expect branch's condition in lockstep — both keyed on !updates.is_empty()","Replace expect with .ok_or_else(|| format!(\"index info unavailable for '{}'\", index)) so it degrades to an error","Restructure to load index_info inside the updates branch: `let info = index_info(client, index).await?;` making the expect unnecessary","Add a test for update-only, insert-only, and empty batches to pin the loading behavior"],"exampleFix":"// before\nlet index_info = index_info.as_ref().expect(\"index info is loaded for document writes\");\n// after\nlet index_info = index_info.as_ref()\n    .ok_or_else(|| format!(\"Meilisearch index info unavailable for '{}' during document writes\", index))?;","handlingStrategy":"try-catch","validationCode":"let idx = client.get_index(&index).await?;\nif idx.primary_key.is_none() {\n    return Err(format!(\"Meilisearch index '{}' has no primary key; set one before updates\", index));\n}","typeGuard":"fn index_supports_updates(info: &IndexInfo) -> bool {\n    info.primary_key.as_deref().map(|k| !k.is_empty()).unwrap_or(false)\n}","tryCatchPattern":"match save_document_batch(client, index, &updates, &inserts).await {\n    Ok(count) => count,\n    Err(e) if e.contains(\"no primary key\") => configure_primary_key_then_retry(),\n    Err(e) => return Err(e),\n}","preventionTips":["Create indexes with an explicit primary key before document writes","Check index metadata (GET /indexes/{uid}) before batch updates","Separate insert-only batches (which skip primary key requirements) from update batches"],"tags":["rust","meilisearch","panic","invariant","index-metadata"],"backgroundTag":"internal-invariant-panic","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}