dbt-labs/dbt-core · error
Invalid config object specified. Keys must be strings
Error message
Invalid config object specified. Keys must be strings
What it means
Companion check to the pair-iteration panic: after successfully iterating the config object's pairs, each key is narrowed to a string with `as_str()` and unwrapped with `expect`. The panic fires when a key in the config dict is not a string (e.g. an integer or boolean key). dbt config keys must be strings to be merged into node config.
Source
Thrown at crates/dbt-jinja-utils/src/phases/parse/resolve_model_context.rs:813
let positional_val: MinijinjaValue = args.next_positional::<MinijinjaValue>()?;
if positional_val.kind() != ValueKind::Map {
return Err(MinijinjaError::new(
MinijinjaErrorKind::InvalidOperation,
format!(
"Invalid config argument kind specified: {}",
positional_val.kind()
),
));
}
positional_val
.as_object()
.unwrap()
.try_iter_pairs()
.expect("Invalid config object specified")
.map(|(k, v)| {
(
k.as_str()
.expect("Invalid config object specified. Keys must be strings")
.to_string(),
v,
)
})
.collect()
} else {
args.drain_kwargs()
};
self.apply_config(state, kwargs)
}
fn call_method(
self: &Arc<Self>,
state: &State<'_, '_>,
name: &str,
args: &[MinijinjaValue],
_listeners: &[Rc<dyn RenderingEventListener>],View on GitHub (pinned to 0267ce9170)
Solutions
- Quote all keys in the config dict: `{{ config({'key': 'value'}) }}` instead of `{{ config({key: 'value'}) }}` with a numeric key
- Convert keys to strings in the macro that builds the config (`key ~ ''` or `key|string`)
- Audit the failing model's config calls for numeric/boolean keys
- At the code level, replace expect with an error that shows the offending key and its type
Example fix
// before (model.sql)
{{ config({1: 'priority'}) }}
// after
{{ config({'1': 'priority'}) }} Defensive patterns
Strategy: validation
Validate before calling
{% for k in cfg.keys() %}
{% if k is not string %}
{{ exceptions.raise_compiler_error("config keys must be strings, got: " ~ type(k)) }}
{% endif %}
{% endfor %} Type guard
// in jinja, coerce keys before building the dict
{% set cfg = {} %}
{% do cfg.update({k|string: v}) %} Try / catch
k.as_str().ok_or_else(|| MinijinjaError::new(
ErrorKind::InvalidOperation,
format!("config keys must be strings, got: {k:?}")))?; Prevention
- Always quote dict keys in {{ config({...}) }} calls
- Coerce dynamic keys with `|string` in macros that build configs
- Avoid numeric literals as config keys
- Lint models for config dicts with unquoted keys
When it happens
Trigger: Calling `{{ config({...}) }}` where the dict has non-string keys, most commonly `{{ config({1: 'value'}) }}` or keys produced by macros using non-string literals; also YAML-injected configs with numeric keys round-tripped into jinja.
Common situations: Programmatic config generation in jinja using numeric keys; quoting mistakes turning keys into numbers; users writing `{{ config({'2024_tags': ...}) }}` vs `{{ config({2024: ...}) }}` style dicts.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Invalid config object specified
- Failed to serialize merged node config to dbt_yaml::Value fo
- get_view_options: Failed to deserialize config: {e}
- get_common_options: Failed to deserialize config: {e}
- role must be a string
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/75869f4df7f3bf5a.
Report an issue: GitHub.