dbt-labs/dbt-core · error

distinct with non-string keys

Error message

distinct with non-string keys

What it means

dbt-agate's Rust Table.distinct() accepts a keys iterable but only supports string key names; iterating the argument and encountering a non-string Value triggers unimplemented!(). Non-string column identifiers are not implemented.

Source

Thrown at crates/dbt-agate/src/table.rs:1375

            //     :returns:
            //     A new :class:`.Table`.
            //     """
            // ```
            "distinct" => {
                let iter = ArgsIter::new("Table.distinct", &[], args);
                let key = iter.next_kwarg::<Option<&Value>>("key")?;
                iter.finish()?;

                let key = match key {
                    Some(v) => {
                        if let Some(s) = v.as_str() {
                            Some(vec![s.to_string()])
                        } else if let Ok(iter) = v.try_iter() {
                            let mut keys = Vec::new();
                            for key in iter {
                                match key.as_str() {
                                    Some(s) => keys.push(s.to_string()),
                                    None => unimplemented!("distinct with non-string keys"),
                                }
                            }
                            Some(keys)
                        } else {
                            None
                        }
                    }
                    None => None,
                };

                let result = self.as_ref().distinct(key).map_err(|e| {
                    Error::new(ErrorKind::InvalidOperation, format!("Table.distinct: {e}"))
                })?;
                Ok(Value::from_object(result))
            }
            other => unimplemented!("AgateTable::{}", other),
        }
    }

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Ensure every element of the keys iterable is a string column name
  2. Convert non-string keys (indices/objects) to column-name strings before the call
  3. Call distinct() with no keys to deduplicate on all columns

Example fix

// before
table.call_method("distinct", kwargs!{"keys" => Value::from(vec![Value::from(0)])})
// after
table.call_method("distinct", kwargs!{"keys" => Value::from(vec![Value::from("id")])})
Defensive patterns

Strategy: validation

Validate before calling

let all_strings = keys.iter().all(|k| k.as_str().is_some());
if !all_strings { panic!("distinct keys must all be strings"); }

Type guard

fn all_str_keys(iter: impl Iterator<Item = Value>) -> bool { iter.all(|k| k.as_str().is_some()) }

Prevention

When it happens

Trigger: Calling table.distinct(keys=[...]) where the keys iterable contains a non-string Value (e.g. an integer column index or a column object) mixed with or instead of strings.

Common situations: Porting Python agate code that used column indices; dynamically building the keys list from non-string sources; passing a single non-string key wrapped in an iterable.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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