diff --git a/crates/polars-plan/src/plans/conversion/expr_expansion.rs b/crates/polars-plan/src/plans/conversion/expr_expansion.rs index 4709641662f9..27aa6bdb6a89 100644 --- a/crates/polars-plan/src/plans/conversion/expr_expansion.rs +++ b/crates/polars-plan/src/plans/conversion/expr_expansion.rs @@ -337,8 +337,43 @@ fn expand_struct_fields( unreachable!() }; let field = input[0].to_field(schema, Context::Default)?; - let DataType::Struct(fields) = field.dtype() else { - polars_bail!(InvalidOperation: "expected 'struct'") + let dtype = field.dtype(); + let DataType::Struct(fields) = dtype else { + if !dtype.is_known() { + let mut msg = String::from( + "expected 'struct' got an unknown data type + +This means there was an operation of which the output data type could not be determined statically. +Try setting the output data type for that operation.", + ); + for e in input[0].into_iter() { + #[allow(clippy::single_match)] + match e { + #[cfg(feature = "list_to_struct")] + Expr::Function { + input: _, + function, + options: _, + } => { + if matches!( + function, + FunctionExpr::ListExpr(ListFunction::ToStruct(..)) + ) { + msg.push_str( + " + +Hint: set 'upper_bound' for 'list.to_struct'.", + ); + } + }, + _ => {}, + } + } + + polars_bail!(InvalidOperation: msg) + } else { + polars_bail!(InvalidOperation: "expected 'struct' got {}", field.dtype()) + } }; // Wildcard. diff --git a/py-polars/polars/expr/list.py b/py-polars/polars/expr/list.py index 4d239460d6b5..208c6538cedf 100644 --- a/py-polars/polars/expr/list.py +++ b/py-polars/polars/expr/list.py @@ -1125,6 +1125,11 @@ def to_struct( Notes ----- + It is recommended to set 'upper_bound' to the correct output size of the struct. + If this is not set, Polars will not know the output type of this operation and + will set it to 'Unknown' which can lead to errors because Polars is not able + to resolve the query. + For performance reasons, the length of the first non-null sublist is used to determine the number of output fields. If the sublists can be of different lengths then `n_field_strategy="max_width"` must be used to obtain the expected