dbt-labs/dbt-core · error

role must be a string

Error message

role must be a string

What it means

This error is raised by the `grant_access_to` adapter method when the `role` argument is provided but is not a Jinja string. The method allows `role` to be None/undefined (no role grant), but if a value is passed it must be coercible to a string via `as_str()`. dbt-core throws the equivalent Python error when `grant_access_to` receives a non-string role.

Source

Thrown at crates/dbt-adapter/src/adapter/mod.rs:2312

                let database = grant_target.project.as_deref().ok_or_else(|| {
                    minijinja::Error::new(
                        minijinja::ErrorKind::InvalidOperation,
                        "project in a GrantAccessToTarget cannot be empty",
                    )
                })?;
                let schema = grant_target.dataset.as_deref().ok_or_else(|| {
                    minijinja::Error::new(
                        minijinja::ErrorKind::InvalidOperation,
                        "dataset in a GrantAccessToTarget cannot be empty",
                    )
                })?;

                let role = if role_val.is_none() || role_val.is_undefined() {
                    None
                } else {
                    Some(role_val.as_str().ok_or_else(|| {
                        minijinja::Error::new(
                            minijinja::ErrorKind::InvalidOperation,
                            "role must be a string",
                        )
                    })?)
                };

                let entity = downcast_value_to_dyn_base_relation(entity_val)?;

                let mut conn =
                    adapter.borrow_tlocal_connection(Some(state), node_id_from_state(state))?;
                let result = adapter.grant_access_to(
                    state,
                    conn.as_mut(),
                    &entity,
                    entity_type,
                    role,
                    database,
                    schema,

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Ensure the role argument is a quoted string, e.g. grant_access_to(relation, privilege, role='reporter')
  2. If the role may be absent, omit the kwarg entirely or pass an actually-undefined variable instead of none
  3. Convert non-string values before calling: role=my_role|string
  4. Inspect the value with log() or print() to confirm its type before the call

Example fix

// before (Jinja)
{{ grant_access_to(relation, 'select', role=none) }}
// after
{{ grant_access_to(relation, 'select', role='reporter') }}
Defensive patterns

Strategy: type-guard

Validate before calling

{% if role is defined and role is not none and role is not string %}
  {{ exceptions.raise_compiler_error("role must be a string, got: " ~ role) }}
{% endif %}

Type guard

{% macro is_jinja_string(v) %}
  {{ return(v is string) }}
{% endmacro %}

Try / catch

{% set res = adapter.dispatch('grant_access_to')(relation, privilege, role=role if role is string else none) %}

Prevention

When it happens

Trigger: Calling `grant_access_to(relation, privilege, role=...)` from a Jinja macro with a non-string role value, e.g. `role=none` that is not actually undefined (a literal none Value rather than undefined), a numeric, boolean, or dict/list value, or a Jinja object that does not implement string conversion.

Common situations: Passing a Jinja `none` literal which is neither undefined nor a string; passing an integer role ID; passing a config-derived value that resolves to a dict; typo where the caller passes a variable holding an object instead of its string name.

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/3bbad8ea1fb32fb5. Report an issue: GitHub.