{"record":{"id":"91f2acbad0f18473","repo":"nautechsystems/nautilus_trader","slug":"prepare-custom-data-batch-called-with-empty-data","errorCode":null,"errorMessage":"prepare_custom_data_batch called with empty data","messagePattern":"prepare_custom_data_batch called with empty data","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/persistence/src/backend/custom.rs","lineNumber":136,"sourceCode":"            for segment in safe.split('/') {\n                components.push(segment.to_string());\n            }\n        }\n    }\n    components\n}\n\n/// Prepares a batch of custom data for writing: encodes to Arrow, augments with `data_type` column,\n/// and returns type identity and timestamp range so the catalog can build path and perform I/O.\n///\n/// # Errors\n///\n/// Returns an error if encoding or augmentation fails, or if the type is not registered.\npub fn prepare_custom_data_batch(\n    data: Vec<CustomData>,\n) -> anyhow::Result<(RecordBatch, String, Option<String>, UnixNanos, UnixNanos)> {\n    let Some(first_custom) = data.first() else {\n        anyhow::bail!(\"prepare_custom_data_batch called with empty data\");\n    };\n\n    let type_name = first_custom.data.type_name();\n    let identifier = first_custom.data_type.identifier().map(String::from);\n    let dt_meta = first_custom.data_type.metadata_string_map();\n    let data_type_json = first_custom\n        .data_type\n        .to_persistence_json()\n        .map_err(|e| anyhow::anyhow!(\"Failed to serialize data_type for persistence: {e}\"))?;\n\n    let start_ts = first_custom.data.ts_init();\n    let end_ts = data.last().map_or(start_ts, |custom| custom.data.ts_init());\n    let items: Vec<Arc<dyn CustomDataTrait>> =\n        data.into_iter().map(|c| Arc::clone(&c.data)).collect();\n\n    let batch = encode_custom_to_arrow(type_name, &items)\n        .map_err(|e| anyhow::anyhow!(\"Failed to encode custom data to Arrow: {e}\"))?\n        .ok_or_else(|| {","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/persistence/src/backend/custom.rs#L118-L154","documentation":"prepare_custom_data_batch requires at least one CustomData item to derive the type name, identifier, metadata, and timestamp bounds from the first element. An empty Vec cannot produce a record batch, so it is rejected upfront rather than failing later during encoding.","triggerScenarios":"Calling write_custom_data_batch with an empty Vec<CustomData>; the writer passes the batch through to prepare_custom_data_batch which bails on data.first() being None.","commonSituations":"A query/backfill produced no rows but the code unconditionally calls the write API, a filtered stream yielded nothing for a session, or an off-by-one slice produced an empty collection.","solutions":["Check data.is_empty() before calling write_custom_data_batch and return early when empty.","Guard the call site so only non-empty batches are written.","If empty writes should be valid, treat them as no-ops at the caller instead of invoking the API."],"exampleFix":"// before\nwrite_custom_data_batch(data)?;\n// after\nif !data.is_empty() {\n    write_custom_data_batch(data)?;\n}","handlingStrategy":"validation","validationCode":"if data.is_empty() {\n    return; // nothing to write — skip the API call entirely\n}\nwrite_custom_data_batch(data)?;","typeGuard":null,"tryCatchPattern":"if let Err(e) = write_custom_data_batch(data) {\n    if e.to_string().contains(\"empty data\") {\n        // treat as no-op; log and continue\n    } else {\n        return Err(e);\n    }\n}","preventionTips":["Early-return on empty collections before any write API call.","Check that upstream filters/queries actually produced rows before writing.","Add an assertion/debug check on batch length in ingestion pipelines."],"tags":["rust","persistence","empty-input","custom-data"],"backgroundTag":"empty-required-field","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}