{"record":{"id":"ebdc1b95eec550ab","repo":"quickwit-oss/quickwit","slug":"index-uid-should-be-a-required-field","errorCode":null,"errorMessage":"`index_uid` should be a required field","messagePattern":"`index_uid` should be a required field","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-serve/src/elasticsearch_api/bulk_v2.rs","lineNumber":186,"sourceCode":"\n#[allow(clippy::result_large_err)]\nfn make_elastic_bulk_response_v2(\n    ingest_response_v2: IngestResponseV2,\n    mut per_subrequest_doc_handles: HashMap<u32, Vec<DocHandle>>,\n    now: Instant,\n    action_count: usize,\n    invalid_index_id_items: Vec<(usize, ElasticBulkItem)>,\n) -> Result<ElasticBulkResponse, ElasticsearchError> {\n    let mut positioned_actions: Vec<(usize, ElasticBulkAction)> = Vec::with_capacity(action_count);\n    let mut errors = false;\n\n    // Populate the items for each `IngestSuccess` subresponse. They may be partially successful and\n    // contain some parse failures.\n    for success in ingest_response_v2.successes {\n        let index_id = success\n            .index_uid\n            .map(|index_uid| index_uid.index_id)\n            .expect(\"`index_uid` should be a required field\");\n\n        // Find the doc handles for the subresponse.\n        let mut doc_handles = remove_doc_handles(\n            &mut per_subrequest_doc_handles,\n            success.subrequest_id,\n        )\n        .inspect_err(|_| {\n            rate_limited_error!(limit_per_min=6, index_id=%index_id, \"could not find subrequest id\");\n        })?;\n        doc_handles.sort_unstable_by_key(|doc_handle| doc_handle.doc_uid);\n\n        // Populate the response items with one error per parse failure.\n        for parse_failure in success.parse_failures {\n            errors = true;\n\n            let failed_doc_uid = parse_failure.doc_uid();\n            let doc_handle_idx = doc_handles\n                .binary_search_by_key(&failed_doc_uid, |doc_handle| doc_handle.doc_uid)","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-serve/src/elasticsearch_api/bulk_v2.rs#L168-L204","documentation":"When building the Elasticsearch-compatible bulk response v2, each IngestSuccess subresponse must carry an index_uid so the code can map results back to per-request doc handles. The gRPC contract marks index_uid as required, so a None here means the ingest service violated the protocol, and the code panics via expect.","triggerScenarios":"elastic_bulk_ingest_v2 receives a v2 ingest response whose successes contain an entry with no index_uid set (ingest service bug or cross-version protocol mismatch).","commonSituations":"Running a quickwit server with mismatched grpc service versions (older ingest service producing v2 responses without index_uid); custom/mocked ingest services in tests returning incomplete successes.","solutions":["Ensure the ingest API service and serve crate versions match","Check that the ingest service populates index_uid on every IngestSuccess","If a custom/mock service is used, set index_uid in all success entries","Consider converting this to a returned error instead of a panic for robustness"],"exampleFix":"// before\nlet index_id = success.index_uid.map(|u| u.index_id).expect(\"`index_uid` should be a required field\");\n// after\nlet index_id = success\n    .index_uid\n    .map(|u| u.index_id)\n    .ok_or_else(|| ElasticsearchError::from(anyhow!(\"ingest success missing index_uid\")))?;","handlingStrategy":"type-guard","validationCode":"// Validate each success entry before consuming:\nfor success in &resp.successes {\n    if success.index_uid.is_none() { return Err(anyhow!(\"ingest success missing index_uid\")); }\n}","typeGuard":"fn has_index_uid(success: &IngestSuccess) -> bool {\n    success.index_uid.is_some()\n}","tryCatchPattern":"// If calling the v2 ingest API through a custom client, wrap:\nlet resp = client.ingest_v2(req).await?;\nif resp.successes.iter().any(|s| s.index_uid.is_none()) {\n    return Err(anyhow!(\"malformed v2 ingest response: missing index_uid\"));\n}","preventionTips":["Use matching client and server versions for the v2 ingest API","Never mock the ingest service without setting required proto fields","Prefer converting protocol violations to errors rather than expects"],"tags":["rust","grpc","bulk-ingest","panic","protocol"],"backgroundTag":"unexpected-response-shape","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}