dbt-labs/dbt-core · error

Table.join: columns must be a sequence of column names

Error message

Table.join: columns must be a sequence of column names: {column} found instead

What it means

Raised in selected_right_table_columns while validating the `columns` argument of Table.join in the Agate table emulation. The Jinja value passed as `columns` is not iterable (not a list/tuple of column-name strings), so try_iter() fails and the underlying type error is wrapped into this InvalidArgument message. The input at fault is the caller's `columns` keyword in the Jinja join() call.

Solutions

  1. Pass a sequence of column-name strings, e.g. table.join(other, left_key='id', right_key='id', columns=['name', 'amount'])
  2. Pass a list of strings rather than a single string, a dict, or an integer
  3. Omit `columns` entirely to select all non-key right-table columns
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/dbt-agate/src/join.rs:118 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at crates/dbt-agate/src/join.rs:118

//
// `None` means "all columns not in right_key" (the default), `Some([])` means
// "no columns", and `Some([name1, name2, ...])` means "only these columns".
fn selected_right_table_columns(columns: Option<&Value>) -> Result<Option<Vec<String>>, Error> {
    let Some(columns) = columns else {
        return Ok(None);
    };
    let iter = columns.try_iter().map_err(|e| {
        Error::new(
            ErrorKind::InvalidArgument,
            format!("Table.join: columns must be a sequence of column names: {e}"),
        )
    })?;
    let mut names = Vec::new();
    for column in iter {
        match column.as_str() {
            Some(name) => names.push(name.to_string()),
            None => {
                return Err(Error::new(
                    ErrorKind::InvalidArgument,
                    format!(
                        "Table.join: columns must be a sequence of column names: {column} found instead"
                    ),
                ));
            }
        }
    }
    Ok(Some(names))
}

impl AgateTable {
    pub fn join(
        &self,
        right_table: &AgateTable,
        left_key: Option<&Value>,
        right_key: Option<&Value>,
        join_type: JoinType,

View on GitHub (pinned to 0267ce9170)