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

  1. Quote all keys in the config dict: `{{ config({'key': 'value'}) }}` instead of `{{ config({key: 'value'}) }}` with a numeric key
  2. Convert keys to strings in the macro that builds the config (`key ~ ''` or `key|string`)
  3. Audit the failing model's config calls for numeric/boolean keys
  4. 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

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


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/75869f4df7f3bf5a. Report an issue: GitHub.