{"record":{"id":"5d3ca2f83cd162b8","repo":"nautechsystems/nautilus_trader","slug":"customdata-json-missing-data-type","errorCode":null,"errorMessage":"CustomData JSON missing data_type","messagePattern":"CustomData JSON missing data_type","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/sql/queries.rs","lineNumber":1626,"sourceCode":"        .map_err(|e| anyhow::anyhow!(\"Failed to load signals: {e}\"))\n    }\n\n    /// Inserts a `CustomData` entry via the provided `pool`.\n    ///\n    /// Serializes the model `CustomData` to full JSON and stores it in the JSONB `value` column.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the SQL INSERT operation fails.\n    pub async fn add_custom_data(pool: &PgPool, data: &CustomData) -> anyhow::Result<()> {\n        let json_bytes = serde_json::to_vec(data)\n            .map_err(|e| anyhow::anyhow!(\"CustomData must be valid JSON: {e}\"))?;\n        let value_json: serde_json::Value = serde_json::from_slice(&json_bytes)\n            .map_err(|e| anyhow::anyhow!(\"CustomData value must be valid JSON: {e}\"))?;\n        let data_type_obj = value_json\n            .get(\"data_type\")\n            .and_then(|v| v.as_object())\n            .ok_or_else(|| anyhow::anyhow!(\"CustomData JSON missing data_type\"))?;\n        let data_type_name = data_type_obj\n            .get(\"type_name\")\n            .and_then(|v| v.as_str())\n            .unwrap_or(\"\");\n        let metadata_json = data_type_obj\n            .get(\"metadata\")\n            .cloned()\n            .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new()));\n        let identifier = data_type_obj\n            .get(\"identifier\")\n            .and_then(|v| v.as_str())\n            .unwrap_or(\"\");\n        sqlx::query(\n            r#\"\n            INSERT INTO \"custom\" (data_type, metadata, identifier, value, ts_event, ts_init, created_at, updated_at)\n            VALUES ($1, $2, $3, $4, $5, $6, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n            ON CONFLICT (id)\n            DO UPDATE SET","sourceCodeStart":1608,"sourceCodeEnd":1644,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/sql/queries.rs#L1608-L1644","documentation":"Raised in `add_custom_data` when the parsed CustomData JSON has no `data_type` object, which the function needs to derive `type_name` and `metadata` columns for the `custom` table. It means the serialized CustomData is structurally incomplete — the `data_type` field is absent or not a JSON object.","triggerScenarios":"Calling `add_custom_data` with a CustomData whose `data_type` field is missing (constructed outside the library's constructors, deserialized from a truncated record, or an older schema without `data_type`).","commonSituations":"Hand-constructing CustomData without its data_type metadata; loading legacy rows saved by an older library version and re-saving them; serde default skipping an unset field.","solutions":["Construct CustomData through its proper constructor so `data_type` is always populated.","Check `data.data_type()` before calling add_custom_data; bail if None/empty.","Migrate legacy records to include `data_type` (type_name and metadata) before re-inserting.","Add a pre-insert validation that `serde_json::to_value(data)?.get(\"data_type\").is_some()`."],"exampleFix":"// before\nadd_custom_data(&pool, &data).await?;\n// after\nlet v = serde_json::to_value(&data)?;\nif v.get(\"data_type\").and_then(|d| d.as_object()).is_none() {\n    anyhow::bail!(\"CustomData missing data_type; refusing insert\");\n}\nadd_custom_data(&pool, &data).await?;","handlingStrategy":"validation","validationCode":"let v = serde_json::to_value(&data)?;\nanyhow::ensure!(\n    v.get(\"data_type\").and_then(|d| d.as_object()).is_some(),\n    \"CustomData must include a data_type object with type_name\"\n);","typeGuard":"fn has_data_type(v: &serde_json::Value) -> bool {\n    v.get(\"data_type\").and_then(|d| d.as_object()).is_some()\n}","tryCatchPattern":null,"preventionTips":["Always construct CustomData via its constructor","Never hand-build or truncate the data_type metadata","Migrate legacy rows lacking data_type before re-inserting","Assert data_type presence in pre-insert validation"],"tags":["json","validation","schema"],"backgroundTag":"schema-validation-failed","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"}