dbt-labs/dbt-core · error · InvalidOperation

Method '{method}' did not return a Table when called on a Ta

Error message

Method '{method}' did not return a Table when called on a TableSet

What it means

AgateTableSet._proxy fans a method call out to every table in the set and expects each underlying table to return an AgateTable. If any table's method result cannot be downcast to an AgateTable, this InvalidOperation error is thrown instead of proceeding. It signals the proxied method produced a value of an unexpected type for at least one member table.

Source

Thrown at crates/dbt-agate/src/table_set.rs:216

    /// def _proxy(self, method_name, *args, **kwargs):
    ///     """
    ///     Calls a method on each table in this :class:`.TableSet`.
    ///     """
    /// ```
    fn _proxy(
        self: &Arc<Self>,
        state: &State<'_, '_>,
        method: &str,
        args: &[Value],
        listeners: &[Rc<dyn RenderingEventListener>],
    ) -> Result<Arc<Self>, Error> {
        let mut tables: Vec<Arc<AgateTable>> = Vec::with_capacity(self.tables.len());
        for table in self.tables.iter() {
            let new_table = table
                .call_method(state, method, args, listeners)?
                .downcast_object::<AgateTable>()
                .ok_or_else(|| {
                    Error::new(
                        ErrorKind::InvalidOperation,
                        format!(
                            "Method '{}' did not return a Table when called on a TableSet",
                            method
                        ),
                    )
                })?;
            tables.push(new_table);
        }
        self._fork(tables, self.keys.clone(), None, None)
    }

    fn tables_as_tuple(self: &Arc<Self>) -> TableSetTablesAsTuple {
        TableSetTablesAsTuple::of_tableset(self)
    }

    fn keys_as_tuple(self: &Arc<Self>) -> TableSetKeysAsTuple {
        TableSetKeysAsTuple::of_tableset(self)

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Verify the method name is one that returns a Table (e.g. aggregate-style table-to-table operations), not a scalar-returning method.
  2. Call the method on individual tables via table.call_method and inspect the returned object type before using TableSet.
  3. If a custom method is registered, ensure it returns an AgateTable-wrapped object.
  4. Check for version mismatches between the method registry and the TableSet proxy code.

Example fix

// before
tables.call_method(state, "column_names", args) // returns a list, not a Table
// after
tables.call_method(state, "join", args) // use a method that returns a Table
Defensive patterns

Strategy: type-guard

Type guard

// verify result is a table before use
let obj = table.call_method(state, method, args)?;
if obj.downcast_object::<AgateTable>().is_none() {
    // handle non-table result, e.g. skip or fall back to per-table calls
}

Prevention

When it happens

Trigger: Calling TableSet.call_method (via _proxy) with a method name whose implementation returns something other than an AgateTable (e.g. a computed column value, a scalar, or a different object type) for one of the tables in the set.

Common situations: Passing a jinja/agate method name that is only valid on non-table objects; a custom or patched method returning a scalar; version drift where a method's return type changed.

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/6aa81d9b21f6eb8e. Report an issue: GitHub.