dbt-labs/dbt-core · error
model_config must be a RelationConfig
Error message
model_config must be a RelationConfig
What it means
An InvalidArgument minijinja error thrown when the optional `model_config` argument to a relation-returning callable is present but is not a RelationConfig object. The code filters out None/undefined and then downcasts the remaining Value to RelationConfig; any other object type (dict, string, relation) fails the downcast.
Source
Thrown at crates/dbt-adapter/src/adapter/mod.rs:3312
/// https://github.com/databricks/dbt-databricks/blob/7c282cabb518a5e1173222e7901896d31de8401f/dbt/adapters/databricks/impl.py#L1088
#[tracing::instrument(skip_all, level = "trace")]
pub fn get_relation_config(
&self,
state: &State,
args: &[Value],
) -> Result<Value, minijinja::Error> {
match &self.inner {
Typed { adapter, .. } => {
let iter =
ArgsIter::new("get_relation_config", &["relation", "model_config"], args);
let relation_val = iter.next_arg::<&Value>()?;
let relation = downcast_value_to_dyn_base_relation(relation_val)?;
let model_config = iter
.next_arg::<Option<&Value>>()?
.filter(|value| !value.is_none() && !value.is_undefined())
.map(|value| {
value.downcast_object::<RelationConfig>().ok_or_else(|| {
minijinja::Error::new(
minijinja::ErrorKind::InvalidArgument,
"model_config must be a RelationConfig",
)
})
})
.transpose()?;
iter.finish()?;
let mut conn =
adapter.borrow_tlocal_connection(Some(state), node_id_from_state(state))?;
let config = adapter.get_relation_config(
state,
conn.as_mut(),
&relation,
model_config.as_deref(),
self.cancellation_token.clone(),
)?;
Ok(Value::from_object(config))View on GitHub (pinned to 0267ce9170)
Solutions
- Pass the model's RelationConfig object (e.g. `model.config` when it is a RelationConfig) as model_config.
- Omit the model_config argument entirely if you have no RelationConfig — it is optional.
- If you only have a dict, first build/obtain a RelationConfig from it before calling.
- Check that the value is not a wrapped or foreign-language object lacking the RelationConfig downcast.
Example fix
// before
load_relation(relation, model_config={'database': 'db'})
// after
load_relation(relation, model_config=model.config) Defensive patterns
Strategy: validation
Validate before calling
{% if model_config is defined and model_config is not none %}
{# must be a RelationConfig, not a dict #}
{% endif %} Type guard
fn is_relation_config(v: &Value) -> bool {
v.downcast_object::<RelationConfig>().is_some()
} Try / catch
match value.downcast_object::<RelationConfig>() { Some(c) => ..., None => return Err(invalid_argument("model_config must be a RelationConfig")) } Prevention
- Only pass objects known to be RelationConfig (e.g. model.config in adapter context)
- Omit the optional argument rather than passing None-like dicts
- Document expected argument types in custom macro signatures
When it happens
Trigger: Calling the adapter function with `model_config=` set to a plain dict, a string, a BaseRelation, or any Value that is not a RelationConfig instance; also occurs if the object was wrapped differently by another binding layer.
Common situations: Custom materializations that pass `config` dicts directly; macros forwarding `this` (a relation) as model_config; refactors that changed what `model_config` holds.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- RelationConfigBaseObject does not support method: {}
- agate_table must be an agate.Table
- {} relation creation from Jinja values
- group_by with function key
- describe_dynamic_table is not supported by the {} adapter
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/73e506d44b9c9d1e.
Report an issue: GitHub.