diesel-rs/diesel · error
Tuples should never implement `ToSql` directly
Error message
Tuples should never implement `ToSql` directly
What it means
Diesel's macro-generated `HasSqlType` implementation for tuples calls `unreachable!` in `metadata()` with this message (comment/branch nominally about tuple `ToSql`). Tuples are supported as row types only via `FromSqlRow`/`Queryable`, not as a directly serializable `ToSql` column type. Hitting this means code attempted to serialize a tuple as a single value.
Solutions
- Use `eq_any` with a Vec for membership tests instead of a tuple equality: `.filter(id.eq_any(vec![1, 2]))`.
- Bind each tuple element to its own column instead of the tuple as a whole.
- If you need a composite value, define a proper struct with diesel's `Queryable`/`Insertable` derives, or use `(a, b)` row patterns only where diesel supports row comparisons with a matching backend.
- Check that you aren't passing a tuple to a single-column insert value slot.
Example fix
// before
users.filter((id, name).eq((1, "a".to_string()))) // tuple used as single ToSql value
// after
users.filter(id.eq(1).and(name.eq("a"))) Defensive patterns
Strategy: type-guard
Validate before calling
// compile-time: don't pass tuples as single-column values // bad: users.filter(id.eq((1, 2))) // good: users.filter(id.eq_any(vec![1, 2]))
Type guard
// only tuples of ToSql fields used via row traits are valid; never call ToSql on a tuple
fn assert_single_column<V: diesel::serialize::ToSql<ST, DB>>(_: &V) {}
// example: assert_single_column(&user_id); // compiles; assert_single_column(&(1, 2)); // does not Prevention
- Use eq_any / per-column filters instead of tuple equality against one column.
- Only use tuple row patterns where diesel's DSL supports them for your backend.
- Model composite values with derives (Queryable/Insertable), not raw tuples as column values.
When it happens
Trigger: Using a tuple like `(a, b)` as a bind parameter / `ToSql` value in a query (e.g. `.eq((1,2))`, `.insert((1,2))` into a single column, or passing a tuple where a single-column value is expected); calling `ToSql` methods on a tuple type directly.
Common situations: Trying to compare a column against a tuple ('IN' misuse) instead of using `eq_any(vec![...])`; mixing up row tuples with column values in `insert_into(...).values(...)`; generated code/dsl misuse in custom backend code.
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.
AI-assisted analysis of diesel-rs/diesel@6fa6ed01b2 (2026-09-07).
Data as JSON: /api/errors/a61071b317c0b889.
Report an issue: GitHub.
Appendix: source
Thrown at diesel/src/type_impls/tuples.rs:59
$($tt)*
};
}
macro_rules! tuple_impls {
($(
$Tuple:tt {
$(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+
}
)+) => {
$(
fake_variadic!{
$Tuple ->
impl<$($T),+, __DB> HasSqlType<($($T,)+)> for __DB where
$(__DB: HasSqlType<$T>),+,
__DB: Backend,
{
fn metadata(_: &mut __DB::MetadataLookup) -> __DB::TypeMetadata {
unreachable!("Tuples should never implement `ToSql` directly");
}
}
}
impl_from_sql_row!(($($T,)+), ($($ST,)+));
fake_variadic! {
$Tuple ->
#[diagnostic::do_not_recommend]
impl<$($T: Expression),+> Expression for ($($T,)+)
where ($($T::SqlType, )*): TypedExpressionType
{
type SqlType = ($(<$T as Expression>::SqlType,)+);
}
}
fake_variadic! {
$Tuple -> impl<$($T: TypedExpressionType,)*> TypedExpressionType for ($($T,)*) {}View on GitHub (pinned to 6fa6ed01b2)