{"record":{"id":"3a94878f08cb1ca7","repo":"dbt-labs/dbt-core","slug":"agatetable-exists","errorCode":null,"errorMessage":"AgateTable exists","messagePattern":"AgateTable exists","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/macro_exec.rs","lineNumber":44,"sourceCode":"    execute_macro_with_package(state, args, macro_name, \"dbt\")\n}\n\npub fn execute_macro_wrapper_with_package(\n    state: &State,\n    args: &[Value],\n    macro_name: &str,\n    package: &str,\n) -> Result<Arc<RecordBatch>, AdapterError> {\n    let result: Value = execute_macro_with_package(state, args, macro_name, package)?;\n    convert_macro_result_to_record_batch(&result)\n}\n\npub fn convert_macro_result_to_record_batch(\n    result: &Value,\n) -> Result<Arc<RecordBatch>, AdapterError> {\n    // Depending on the macro impl, result can be either ResultObject or AgateTable\n    let table = if let Some(result) = result.downcast_object::<ResultObject>() {\n        result.table.as_ref().expect(\"AgateTable exists\").to_owned()\n    } else if let Some(result) = result.downcast_object::<AgateTable>() {\n        result.as_ref().to_owned()\n    } else {\n        return Err(AdapterError::new(\n            AdapterErrorKind::UnexpectedResult,\n            format!(\"Unexpected result type {result}\"),\n        ));\n    };\n\n    let record_batch = table.original_record_batch();\n    Ok(record_batch)\n}\n\npub fn execute_macro_with_package(\n    state: &State,\n    args: &[Value],\n    macro_name: &str,\n    package: &str,","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/macro_exec.rs#L26-L62","documentation":"`convert_macro_result_to_record_batch` (crates/dbt-adapter/src/macro_exec.rs:44) expects a macro result dynamic object to be either a `ResultObject` with a non-None `table` or an `AgateTable`. The `.expect(\"AgateTable exists\")` panics when a `ResultObject` is returned whose `table` field is None — i.e. the macro produced a result without a table.","triggerScenarios":"Calling any of `get_columns_in_relation_via_macro`, `try_columns_from_json_describe`, `execute_macro_wrapper_with_package`, `fetch_json_metadata`, `fetch_catalog_data` when the executed macro returns a `ResultObject` with `table: None` (or an unsupported third type, which yields the UnexpectedResult error instead).","commonSituations":"A custom/dispatched macro variant returns a plain value or empty result instead of a table; a macro was refactored to return JSON metadata without a table; adapter package versions disagree on the macro's return shape.","solutions":["Fix the macro to always set a table on its `ResultObject`","Change the code to handle `result.table` being None by returning an `AdapterError` (or empty RecordBatch) instead of `.expect`","Check which macro implementation was dispatched and verify its return contract","Guard callers: only invoke this conversion for macros documented to return tables"],"exampleFix":"// before\nresult.table.as_ref().expect(\"AgateTable exists\").to_owned()\n// after\nresult.table.as_ref().map(|t| t.to_owned()).ok_or_else(|| {\n    AdapterError::new(AdapterErrorKind::UnexpectedResult, \"macro ResultObject has no table\".into())\n})?","handlingStrategy":"type-guard","validationCode":"// check macro result shape before conversion\nlet ok = result.downcast_object::<ResultObject>().map(|r| r.table.is_some()).unwrap_or(false)\n    || result.downcast_object::<AgateTable>().is_some();\nif !ok { return Err(/* UnexpectedResult */); }","typeGuard":"fn has_convertible_table(result: &Value) -> bool {\n    result.downcast_object::<ResultObject>().map_or(false, |r| r.table.is_some())\n        || result.downcast_object::<AgateTable>().is_some()\n}","tryCatchPattern":"std::panic::catch_unwind(AssertUnwindSafe(|| convert_macro_result_to_record_batch(&result)))","preventionTips":["Ensure dispatched macros always set ResultObject.table","Document each macro's return contract","Test custom macros through the adapter wrapper","Handle the UnexpectedResult branch before calling conversion"],"tags":["rust","panic","macro","record-batch"],"backgroundTag":"internal-invariant-violation","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}