{"record":{"id":"ee5a9c7b08f0dd26","repo":"dbt-labs/dbt-core","slug":"relationconfig-should-deserialize","errorCode":null,"errorMessage":"RelationConfig should deserialize","messagePattern":"RelationConfig should deserialize","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/time_machine/serializable_impls.rs","lineNumber":462,"sourceCode":"            (\n                RelationType::MaterializedView,\n                serde_json::json!({\n                    \"partitioned_by\": {\"partition_by\": [\"event_date\"]}\n                }),\n            ),\n            (\n                RelationType::StreamingTable,\n                serde_json::json!({\n                    \"comment\": {\"comment\": \"streaming events\", \"persist\": true},\n                    \"partitioned_by\": {\"partition_by\": [\"event_date\"]}\n                }),\n            ),\n        ];\n\n        for (relation_type, payload) in cases {\n            let ctx = databricks_ctx(relation_type);\n            let original = RelationConfig::from_time_machine_json(&payload, &ctx)\n                .expect(\"RelationConfig should deserialize\");\n            let recorded = serialize_object(&original).expect(\"RelationConfig should serialize\");\n            let restored = json_to_value_with_context(&recorded, &ctx);\n            assert_eq!(\n                serialize_object(&restored),\n                Some(recorded),\n                \"RelationConfig should roundtrip for {relation_type:?}\"\n            );\n        }\n    }\n\n    /// Reproduces the Databricks same-version replay break.\n    ///\n    /// `tbl_properties::to_jinja` emits TWO keys for the `tblproperties` component:\n    /// the `tblproperties` sub-map (with `pipelines.pipelineId` filtered out) AND a\n    /// separate top-level `pipeline_id`. But `component_from_recorded` only reads back\n    /// `val.get(\"tblproperties\")` — the `pipeline_id` is silently dropped on deserialize.\n    ///\n    /// For any DLT-backed relation (streaming tables, materialized views), the recorded","sourceCodeStart":444,"sourceCodeEnd":480,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/time_machine/serializable_impls.rs#L444-L480","documentation":"Test panic from `RelationConfig::from_time_machine_json(&payload, &ctx).expect(\"RelationConfig should deserialize\")` in serializable_impls.rs:462. RelationConfig is replayed from recorded JSON using a ReplayCallContext (here a Databricks context with a specific RelationType); deserialization fails when the recorded payload does not match what the context-dependent parser expects for that relation type. The time-machine layer throws this because a config payload it recorded can no longer be reconstructed, breaking replay.","triggerScenarios":"Calling `RelationConfig::from_time_machine_json` with a Databricks ctx whose RelationType (Table, View, MaterializedView, StreamingTable) has no matching component parser, or whose payload keys (column_comments, column_tags, comment, tags, tblproperties, partitioned_by) are malformed or unexpected for that type.","commonSituations":"Adding a new Databricks relation type or config component without updating the time-machine registry; changing the nested component JSON shape (e.g. {\"set_tags\": {...}}) so the parser no longer recognizes it; replaying recordings produced by another dbt version.","solutions":["Log the serde/parser error from from_time_machine_json along with the payload and relation type to find the mismatched component.","Check that every component key in relation_config_payload() has a registered parser for each tested RelationType in the Databricks context.","Update component_from_recorded parsing to cover the new/renamed component key.","Regenerate recordings with the current adapter version before replaying."],"exampleFix":"// before\nlet original = RelationConfig::from_time_machine_json(&payload, &ctx)\n    .expect(\"RelationConfig should deserialize\");\n// after\nlet original = RelationConfig::from_time_machine_json(&payload, &ctx)\n    .unwrap_or_else(|e| panic!(\"deserialize failed for {relation_type:?}: {e}; payload={payload}\"));","handlingStrategy":"validation","validationCode":"// before replay\nif !payload.get(\"tblproperties\").map_or(true, |v| v.is_object()) {\n    return Err(\"malformed RelationConfig payload for relation type\");\n}","typeGuard":"fn is_recorded_relation_config(j: &serde_json::Value) -> bool {\n    j.is_object() && KNOWN_COMPONENT_KEYS.iter().any(|k| j.get(k).is_some())\n}","tryCatchPattern":"match RelationConfig::from_time_machine_json(&payload, &ctx) {\n    Ok(cfg) => cfg,\n    Err(e) => { log::error(\"replay config decode failed ({relation_type:?}): {e}\"); skip_replay_step() }\n}","preventionTips":["Register a parser for every new Databricks relation type / component key.","Regenerate recordings after changing RelationConfig components.","Assert roundtrip equality in unit tests for all relation types."],"tags":["rust","databricks","relation-config","replay"],"backgroundTag":"schema-validation-failed","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}