risingwavelabs/risingwave · error
argument of must be boolean, not type
Error message
argument of {} must be boolean, not type {:?} What it means
Thrown by `enforce_bool_clause` when an expression used in a position that requires a boolean (e.g. the ON condition of a join, a filter, or a CASE/HAVING clause) has a non-boolean return type and is not untyped (untyped expressions are implicitly cast to BOOLEAN instead). The error reports the clause name and the actual return type, so it is essentially a static type check failure during query binding/planning.
Solutions
- Add an explicit boolean comparison: change `WHERE flag_col` to `WHERE flag_col = TRUE` (or `<> 0` for integer flags).
- For join conditions, use a proper predicate: `JOIN t ON a.id = b.id AND a.enabled`.
- Cast once at the source: wrap the expression with `::boolean` or `(expr)::boolean = TRUE` if the underlying type is intentionally convertible.
- Fix the UDF/subquery return type to BOOLEAN if the expression author intended a predicate.
Example fix
-- before SELECT * FROM t JOIN u ON t.id u.id; -- after SELECT * FROM t JOIN u ON t.id = u.id;
Defensive patterns
Strategy: validation
Validate before calling
-- check the clause expression type ahead of running the query -- (in app code) ensure the predicate column is BOOLEAN before composing WHERE clauses SELECT data_type FROM information_schema.columns WHERE table_name = 't' AND column_name = 'flag_col';
Try / catch
match enforce_bool_clause(expr, "WHERE") {
Ok(e) => proceed(e),
Err(ExprError::BoolCheck(clause, ty)) => {
// auto-wrap: expr.cast_implicit(DataType::Boolean) or reject query with clear message
}
Err(e) => return Err(e),
} Prevention
- Never rely on integer/string truthiness; always write explicit `= TRUE` / `<> 0` comparisons.
- Design UDFs used in filters to return BOOLEAN.
- Check column types with information_schema before generating predicates in ORMs/query builders.
- Use parenthesization to guarantee operator precedence in complex boolean clauses.
When it happens
Trigger: Writing a WHERE/ON/HAVING/CHECK clause whose expression evaluates to a non-boolean type, e.g. `WHERE 1` with a typed integer column expression, `JOIN t ON a.some_int = b.some_int` without comparison, or using a string/UDTF expression directly as a predicate.
Common situations: Porting MySQL-style `WHERE col` truthiness idioms that Postgres/RisingWave rejects; forgetting the comparison operator (`ON a.id b.id`); a UDF or subquery returning INTEGER/TEXT used directly as a filter; accidental operator precedence making the clause a non-boolean expression.
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
- {0}
- {0}
- AFTER MATCH SKIP TO FIRST/LAST missing its target variable
- ALTER SINK_RATE_LIMIT is not for sink into table
- ALTER SOURCE_RATE_LIMIT is not for table without source
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/a78ec74b3d9b0e16.
Report an issue: GitHub.
Appendix: source
Thrown at src/frontend/src/expr/mod.rs:396
return Err(ErrorCode::BindError(
"could not determine polymorphic type because input has type unknown".into(),
));
}
match self.return_type() {
DataType::Map(m) => Ok(m),
t => Err(ErrorCode::BindError(format!("expects map but got {t}"))),
}
}
/// Shorthand to enforce implicit cast to boolean
pub fn enforce_bool_clause(self, clause: &str) -> RwResult<ExprImpl> {
if self.is_untyped() {
let inner = self.cast_implicit(&DataType::Boolean)?;
return Ok(inner);
}
let return_type = self.return_type();
if return_type != DataType::Boolean {
bail!(
"argument of {} must be boolean, not type {:?}",
clause,
return_type
)
}
Ok(self)
}
/// Create "cast" expr to string (`varchar`) type. This is different from a real cast, as
/// boolean is converted to a single char rather than full word.
///
/// Choose between `cast_output` and `cast_{assign,explicit}(Varchar)` based on `PostgreSQL`'s
/// behavior on bools. For example, `concat(':', true)` is `:t` but `':' || true` is `:true`.
/// All other types have the same behavior when formatting to output and casting to string.
///
/// References in `PostgreSQL`:
/// * [cast](https://github.com/postgres/postgres/blob/a3ff08e0b08dbfeb777ccfa8f13ebaa95d064c04/src/include/catalog/pg_cast.dat#L437-L444)
/// * [impl](https://github.com/postgres/postgres/blob/27b77ecf9f4d5be211900eda54d8155ada50d696/src/backend/utils/adt/bool.c#L204-L209)View on GitHub (pinned to 6469eb736d)