windmill-labs/windmill · error

Mixed types in array

Error message

Mixed types in array

What it means

When converting a JSON array argument to a single-typed PostgreSQL array, `map_as_single_type` maps every element through the same converter `f`. If any conversion returns `None`, the elements are of mixed/incompatible types for the target array type, and the collect yields None -> this error. PostgreSQL arrays are homogeneous, so mixed element types cannot be bound as one parameter.

Source

Thrown at backend/windmill-worker/src/pg_executor.rs:1235

fn map_as_single_type<T>(
    vec: Option<&Vec<Value>>,
    f: impl Fn(&Value) -> Option<T>,
) -> anyhow::Result<Option<Vec<Option<T>>>> {
    if let Some(vec) = vec {
        Ok(Some(
            vec.into_iter()
                .map(|v| {
                    // first option is if the value is of the right type (if none, will stop the collection and throw error)
                    // second option is if the value is null
                    // allow nulls in arrays
                    if matches!(v, Value::Null) {
                        Some(None)
                    } else {
                        f(v).map(Some)
                    }
                })
                .collect::<Option<Vec<Option<T>>>>()
                .ok_or_else(|| anyhow::anyhow!("Mixed types in array"))?,
        ))
    } else {
        Ok(None)
    }
}

/// A boxed `ToSql` value paired with the Postgres `Type` that matches its
/// concrete Rust type. Returned by `convert_val` / `convert_vec_val` so the
/// dispatch in `do_postgresql_inner` always asserts the type that the encoder
/// can actually produce — never a parser-derived guess that drifts from the
/// runtime binding.
type ConvertedParam = (Box<dyn ToSql + Sync + Send>, Type);

fn convert_vec_val(
    vec: Option<&Vec<Value>>,
    arg_t: &String,
) -> windmill_common::error::Result<ConvertedParam> {
    match arg_t.as_str() {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Normalize the array in your script before passing it: ensure every element is the same type (e.g. map all to numbers or strings).
  2. If nulls are intentional, confirm the declared element type allows NULL and elements are otherwise homogeneous.
  3. Declare the correct arg otyp (e.g. `text[]` vs `int[]`) matching your actual data.
  4. Flatten nested arrays into one level, or pass JSONB instead of an array type and cast in SQL.

Example fix

// before: mixed types
const ids = [1, "2", "3"];  // int[] fails
// after
const ids = [1, 2, 3]; // or ["1","2","3"] for text[]
Defensive patterns

Strategy: validation

Validate before calling

// validate homogeneity before passing an array arg
function isHomogeneous(arr) {
  const kinds = new Set(arr.map(v => v === null ? 'null' : typeof v));
  return kinds.size <= 2 && !(kinds.has('object'));
}
// throw if !isHomogeneous(myArray)

Type guard

fn all_same_type(v: &[serde_json::Value], f: impl Fn(&serde_json::Value) -> Option<T>) -> bool {
    v.iter().all(|x| f(x).is_some())
}

Prevention

When it happens

Trigger: `convert_vec_val` handling an array arg whose elements don't all fit the resolved element type — e.g. `[1, "two", null-ish object]` against `int[]`, or a heterogeneous JSON array bound to any declared array type.

Common situations: Users passing `[1, '2', 'three']` from JS/Python where strings and numbers mix; JSON input from webhooks with inconsistent element shapes; nested arrays where a flat array type was declared; objects accidentally included in an otherwise numeric array.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/04b0c1862bcc8efd. Report an issue: GitHub.