dbt-labs/dbt-core · error
Table.rename: column_names must be a map or an array
Error message
Table.rename: column_names must be a map or an array
What it means
Table.rename was called with a column_names argument that is neither a map (old-name -> new-name) nor an array of new column names. The library attempts to interpret the value as a generic iterable when it is not a map, and throws this error if the value cannot be iterated at all. This mirrors agate's Python rename(), which accepts only mappings or sequences for column_names.
Source
Thrown at crates/dbt-agate/src/table.rs:679
if key.as_str().is_some_and(|k| k == col) {
renamed[i] = value.to_string();
}
}
}
renamed
}};
}
if let Some(map) = v.downcast_object_ref::<ValueMap>() {
Ok(rename_columns_by_map!(map))
} else if let Some(map) = v.downcast_object_ref::<MutableMap>() {
let map: ValueMap = map.clone().into();
Ok(rename_columns_by_map!(map))
} else {
// Try to treat it as a generic iterable
let iter = match v.try_iter() {
Ok(iter) => iter,
Err(_) => {
return Err(Error::new(
ErrorKind::InvalidArgument,
"Table.rename: column_names must be a map or an array",
));
}
};
let mut renamed = old;
for (i, value) in iter.enumerate() {
if i >= renamed.len() {
break;
}
if let Some(s) = value.as_str() {
renamed[i] = s.to_string();
} else {
return Err(Error::new(
ErrorKind::InvalidArgument,
format!(
"Table.rename: column_names array must contain only strings, found: {}",
valueView on GitHub (pinned to 0267ce9170)
Solutions
- Pass column_names as a map of old column name to new column name, e.g. {"old": "new"}.
- Pass column_names as a plain array of new column name strings, one per column.
- Omit column_names entirely if you only intend to rename rows.
Example fix
// before
table.rename("new_name", None, false, false)
// after
table.rename(Some(vec!["new_name"]), None, false, false) Defensive patterns
Strategy: validation
Validate before calling
fn valid_column_names(v: &Value) -> bool {
v.as_map().is_some()
|| v.as_array().map_or(false, |a| a.iter().all(|x| x.as_str().is_some()))
} Type guard
fn is_name_spec(v: &Value) -> bool {
v.as_map().is_some()
|| v.as_array().map_or(false, |a| a.iter().all(|x| x.as_str().is_some()))
} Try / catch
match table.rename(column_names, row_names, false, false) {
Ok(t) => t,
Err(e) if e.to_string().contains("column_names must be a map or an array") => {
eprintln!("column_names must be a map or array of strings: {e}");
table.rename(default_names, None, false, false)?
}
Err(e) => return Err(e),
} Prevention
- Always pass a map or a Vec<String> for column_names.
- Never pass a bare string where an array of names is expected.
- Validate argument types before calling rename, especially when values come from config or JSON.
When it happens
Trigger: Calling table.rename with column_names set to a scalar (e.g. a string instead of an array of strings), an integer, a struct without iteration support, or a non-iterable value instead of a map/array.
Common situations: Passing a single string like "col_a" instead of ["col_a", "col_b"]; passing a hash-map-like object that the bridge cannot convert; porting Python agate code where column_names was a dict and the Rust binding expects a map or array.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Table.rename: row_names must be a map or an array
- Table.rename: column_names array must contain only strings,
- Table.rename: row_names array must contain only strings, fou
- Table.rename: renamed_columns length ({}) does not match num
- Table.rename: renamed_rows length ({}) does not match number
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/95cf14ba7ea97d10.
Report an issue: GitHub.