{"record":{"id":"e7d26342736a2dfd","repo":"clockworklabs/SpacetimeDB","slug":"heterogeneous-array","errorCode":null,"errorMessage":"heterogeneous array","messagePattern":"heterogeneous array","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/sats/src/algebraic_value/ser.rs","lineNumber":248,"sourceCode":"}\n\n/// Continuation for serializing an array.\npub struct SerializeArrayValue {\n    /// For efficiency, the first time `serialize_element` is done,\n    /// this is used to allocate with capacity.\n    len: Option<usize>,\n    /// The array being built.\n    array: ArrayValueBuilder,\n}\n\nimpl ser::SerializeArray for SerializeArrayValue {\n    type Ok = AlgebraicValue;\n    type Error = <ValueSerializer as ser::Serializer>::Error;\n\n    fn serialize_element<T: ser::Serialize + ?Sized>(&mut self, elem: &T) -> Result<(), Self::Error> {\n        self.array\n            .push(value_serialize(elem), self.len.take())\n            .expect(\"heterogeneous array\");\n        Ok(())\n    }\n\n    fn end(self) -> Result<Self::Ok, Self::Error> {\n        Ok(ArrayValue::from(self.array).into())\n    }\n}\n\n/// A builder for [`ArrayValue`]s\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]\nenum ArrayValueBuilder {\n    /// An array of [`SumValue`](crate::SumValue)s.\n    Sum(Vec<crate::SumValue>),\n    /// An array of [`ProductValue`](crate::ProductValue)s.\n    Product(Vec<crate::ProductValue>),\n    /// An array of [`bool`]s.\n    Bool(Vec<bool>),\n    /// An array of [`i8`]s.","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/sats/src/algebraic_value/ser.rs#L230-L266","documentation":"Panic in SpacetimeDB's serde integration for `AlgebraicValue`. SatS arrays are strictly homogeneous; when a type is serialized through `SerializeArrayValue`, every element must serialize to the same primitive kind. `ArrayValueBuilder::push` enforces this and `expect(\"heterogeneous array\")` fires when element N has a different value type than elements 0..N-1.","triggerScenarios":"Serializing a Rust sequence (Vec/array/struct with `deserialize_with`-style element visitors, or `serde_json::Value`-like dynamic data) into an `AlgebraicValue` where elements serialize to different kinds — e.g. first element serializes as an integer and the second as a string or a sum variant. Common with `serialize_any`-style serializers, enums with differing variant payloads, or serde_json `Value::Array` mixing number/string/bool.","commonSituations":"Storing JSON-ish dynamic data (`serde_json::Value`) in a SatS `array value` column; custom `Serialize` impls that emit different shapes per element; converting between formats where JSON allows heterogeneous arrays but SatS does not; RPC/subscription payloads round-tripping untyped JSON.","solutions":["Normalize the data before serialization: map every element to one type (e.g. stringify scalars, or wrap in a sum type with uniform variants).","Define a proper SatS schema element type (product or sum type) so all elements serialize identically, instead of relying on dynamic/any serialization.","If consuming JSON, pre-validate the array with a check that all elements share one JSON kind before converting to `AlgebraicValue`.","For custom Serialize impls, ensure element serialization is uniform — heterogeneous logic belongs in a sum type with named variants."],"exampleFix":"// before: heterogeneous JSON array -> panics\nlet v: AlgebraicValue = value_serialize(&serde_json::json!([1, \"two\", true]));\n\n// after: normalize all elements to one shape first\nlet v: AlgebraicValue = value_serialize(&vec![\"1\", \"two\", \"true\"]); // homogeneous strings","handlingStrategy":"type-guard","validationCode":"// Before serializing dynamic data, assert all elements share one value kind\nfn homogeneous(vals: &[serde_json::Value]) -> bool {\n    match vals.split_first() {\n        Some((f, rest)) => rest.iter().all(|v| std::mem::discriminant(v) == std::mem::discriminant(f)),\n        None => true,\n    }\n}","typeGuard":"fn uniform_array(v: &serde_json::Value) -> Option<&Vec<serde_json::Value>> {\n    v.as_array().filter(|a| homogeneous(a))\n}","tryCatchPattern":"let r = std::panic::catch_unwind(AssertUnwindSafe(|| value_serialize(&data)));\nmatch r { Ok(v) => v, Err(_) => return Err(SerializationError::heterogeneous_array) }","preventionTips":["Never store untyped JSON arrays directly; declare an explicit element type in the module schema.","Normalize mixed-kind arrays (stringify, or wrap in a sum type) before conversion to AlgebraicValue.","Unit-test serialization of every dynamic/dynamic-ish payload your module accepts."],"tags":["rust","spacetimedb","serde","serialization","type-mismatch","panic"],"backgroundTag":"type-mismatch-in-array","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}