dbt-labs/dbt-core · error
Table.distinct: error creating grouper
Error message
Table.distinct: error creating grouper: {e} What it means
distinct builds a grouper over the specified (or all) columns to identify unique rows. If grouper construction fails — unknown column name or unsupported column type — the error is wrapped as 'Table.distinct: error creating grouper'.
Solutions
- Verify every name in the subset list exists in the table's column_names.
- Call distinct with no subset to dedupe on all columns, or restrict the subset to primitive-typed columns.
- Read the wrapped inner error for the precise cause (missing column vs unsupported dtype) and fix the input accordingly.
Example fix
// before table.distinct(Some(vec!["user_id", "evnt_date"]), None)? // typo // after table.distinct(Some(vec!["user_id", "event_date"]), None)?
Defensive patterns
Strategy: validation
Validate before calling
let names = table.column_names();
if let Some(sub) = &subset {
for c in sub {
if !names.contains(c) { return Err(format!("column '{c}' not found")); }
}
} Try / catch
match table.distinct(subset, key_type) {
Ok(t) => t,
Err(e) if e.to_string().contains("error creating grouper") => {
eprintln!("{e}");
// fix subset names/types and retry, or dedupe on all columns
table.distinct(None, None)
}
Err(e) => return Err(e),
} Prevention
- Validate subset names against table.column_names() before calling distinct.
- Restrict distinct subsets to primitive-typed columns.
- Reread schema after upstream transformations that may rename columns.
When it happens
Trigger: Calling table.distinct with a subset argument naming a column that does not exist, or distinct over columns with types the grouper cannot hash (e.g. nested types).
Common situations: Typo in the subset column list; upstream schema drift renamed a column; deduplicating on struct/list columns unsupported by the grouper.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/0efba4b099b654af.
Report an issue: GitHub.
Appendix: source
Thrown at crates/dbt-agate/src/table.rs:948
fn distinct(&self, key: Option<Vec<String>>) -> Result<AgateTable, Error> {
let column_names = match key {
Some(keys) => self
.column_names()
.iter()
.filter_map(|key| {
if keys.contains(key) {
Some(key.clone())
} else {
None
}
})
.collect(),
None => self.column_names(),
};
// TODO: cast the values in `column` according to `key_type`, create a new
// table with the casted column, and use that table to create the grouper
let grouper = self.grouper(&column_names).map_err(|e| {
Error::new(
ErrorKind::InvalidOperation,
format!("Table.distinct: error creating grouper: {e}"),
)
})?;
// Builds a selection vector with the first `row_idx` of every group.
// A "group" is the set of rows that are identical, so whenever the
// grouper emits a `group_id` we've already seen, we know we shouldn't
// emit that `row_idx` because it's not the index of a distinct row.
let mut seen_groups = HashSet::new();
let indices: Vec<u64> = grouper
.iter()
.enumerate()
.filter_map(|(row_idx, group_id)| {
if seen_groups.insert(group_id) {
Some(row_idx as u64)
} else {
NoneView on GitHub (pinned to 0267ce9170)