risingwavelabs/risingwave · error

expects a struct array ref

Error message

expects a struct array ref

What it means

The field-access expression's array evaluation expects its evaluated input to be a StructArray; anything else (e.g. a ListArray or other variant) triggers this anyhow-based error. It indicates the input expression's type didn't match the struct-field-access requirement.

Source

Thrown at src/expr/impl/src/scalar/field.rs:45

pub struct FieldExpression<E> {
    return_type: DataType,
    input: E,
    index: usize,
}

impl<E: ExpressionInfo> ExpressionInfo for FieldExpression<E> {
    fn return_type(&self) -> DataType {
        self.return_type.clone()
    }
}

macro_rules! eval_field {
    ($mode:ident, $this:expr, $input:expr) => {{
        let array = risingwave_expr::forward!($mode, $this.input, eval($input))?;
        if let ArrayImpl::Struct(struct_array) = array.as_ref() {
            Ok(struct_array.field_at($this.index).clone())
        } else {
            Err(anyhow!("expects a struct array ref").into())
        }
    }};
}

macro_rules! eval_row_field {
    ($mode:ident, $this:expr, $input:expr) => {{
        let struct_datum = risingwave_expr::forward!($mode, $this.input, eval_row($input))?;
        struct_datum
            .map(|s| match s {
                ScalarImpl::Struct(v) => Ok(v.fields()[$this.index].clone()),
                _ => Err(anyhow!("expects a struct array ref").into()),
            })
            .transpose()
            .map(|x| x.flatten())
    }};
}

impl<E: SyncExpression> SyncExpression for FieldExpression<E> {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the input expression's return type is a STRUCT before applying field access.
  2. Unnest lists first (e.g. FLATTEN/unnest) before accessing struct fields inside them.
  3. Verify the source schema: the accessed column must genuinely be a struct type.

Example fix

// before
field_access(input_list_expr, index) // input is ListArray
// after
field_access(unnest_or_cast_to_struct(input_list_expr), index)
Defensive patterns

Strategy: validation

Validate before calling

if !matches!(input.return_type(), DataType::Struct(_)) {
    return Err("field access requires a struct input".into());
}

Type guard

fn is_struct_input(e: &BoxedExpression) -> bool { matches!(e.return_type(), DataType::Struct(_)) }

Prevention

When it happens

Trigger: Evaluating a field access expression whose input array is not ArrayImpl::Struct — e.g. accessing `.field` on a non-struct column, or after a cast/reorganization changed the input to a list or primitive array.

Common situations: Querying a nested field of a column that is actually a list/array rather than a struct; schema drift where a struct column became a different type; incorrect expr construction in custom planners.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/b103c6c420b34127. Report an issue: GitHub.