{"record":{"id":"e4fc44dce6cc1119","repo":"nautechsystems/nautilus_trader","slug":"cannot-write-type-name-data-with-mixed-identitie","errorCode":null,"errorMessage":"Cannot write {type_name} data with mixed identities: element {position} has metadata {:?} but the first element has {first_metadata:?}; write each instrument or bar type separately","messagePattern":"Cannot write (.+?) data with mixed identities: element (.+?) has metadata (.+?) but the first element has (.+?); write each instrument or bar type separately","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/persistence/src/backend/catalog.rs","lineNumber":604,"sourceCode":"    ) -> anyhow::Result<PathBuf>\n    where\n        T: HasTsInit + EncodeToRecordBatch + CatalogPathPrefix,\n    {\n        if data.is_empty() {\n            return Ok(PathBuf::new());\n        }\n\n        let type_name = to_snake_case(std::any::type_name::<T>());\n        Self::check_ascending_timestamps(data, &type_name)?;\n\n        // The write directory and schema metadata come from the first element,\n        // so mixed identities would silently re-label everything after it\n        let first_metadata = data[0].metadata();\n        if let Some(position) = data\n            .iter()\n            .position(|item| item.metadata() != first_metadata)\n        {\n            anyhow::bail!(\n                \"Cannot write {type_name} data with mixed identities: element {position} has \\\n                 metadata {:?} but the first element has {first_metadata:?}; write each \\\n                 instrument or bar type separately\",\n                data[position].metadata(),\n            );\n        }\n\n        let start_ts = start.unwrap_or(data.first().unwrap().ts_init());\n        let end_ts = end.unwrap_or(data.last().unwrap().ts_init());\n\n        let batches = self.data_to_record_batches(data)?;\n        let schema = batches.first().expect(\"Batches are empty.\").schema();\n\n        let identifier = if T::path_prefix() == \"bars\" {\n            schema.metadata.get(\"bar_type\").cloned()\n        } else {\n            schema.metadata.get(\"instrument_id\").cloned()\n        };","sourceCodeStart":586,"sourceCodeEnd":622,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/persistence/src/backend/catalog.rs#L586-L622","documentation":"`ParquetDataCatalog::write_to_parquet` requires all elements in one write batch to share identical metadata (instrument/bar identity encoded in `ts_init`-side metadata). If any element's metadata differs from the first element's, the write aborts because later rows would be silently re-labeled with the first element's identity, corrupting the file.","triggerScenarios":"Calling `write_to_parquet` (directly or via helpers like quote/trade/funding writers) with a slice that mixes instruments, bar types, or otherwise differing metadata within one batch.","commonSituations":"Batching data for multiple symbols or bar types into a single write call; replaying a mixed stream into one buffer; concatenating buffers from different instruments before writing.","solutions":["Group the data by metadata (instrument ID / bar type) and issue one `write_to_parquet` call per group.","If streaming, flush and start a new write whenever metadata changes mid-stream.","Inspect `data[i].metadata()` across the batch to find the first divergent element and split there."],"exampleFix":"// before\ncatalog.write_to_parquet(&mixed_quotes, type_name, None, None, None, None, None)?;\n// after\nfor (_meta, group) in group_quotes_by_metadata(&mixed_quotes) {\n    catalog.write_to_parquet(&group, type_name, None, None, None, None, None)?;\n}","handlingStrategy":"validation","validationCode":"fn all_same_metadata(data: &[impl MetadataAware]) -> bool {\n    let first = data[0].metadata();\n    data.iter().all(|d| d.metadata() == first)\n}","typeGuard":null,"tryCatchPattern":"match catalog.write_to_parquet(&batch, type_name, None, None, None, None, None) {\n    Err(e) if e.to_string().contains(\"mixed identities\") => {\n        for group in split_by_metadata(&batch) { write_group(group)?; }\n    }\n    other => other?,\n}","preventionTips":["Never batch multiple instruments/bar types into one write call","Group by metadata upstream before persisting","Flush and restart the write on stream metadata changes","Assert metadata homogeneity in ingestion tests"],"tags":["rust","persistence","parquet","validation","metadata"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}