{"record":{"id":"35441a111e7c39de","repo":"risingwavelabs/risingwave","slug":"expect-struct","errorCode":null,"errorMessage":"expect struct<...>","messagePattern":"expect struct<\\.\\.\\.>","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/src/types/struct_type.rs","lineNumber":271,"sourceCode":"                f,\n                \"struct<{}>\",\n                self.iter()\n                    .map(|(name, ty)| format!(\"{} {}\", QuoteIdent(name), ty))\n                    .join(\", \")\n            )\n        }\n    }\n}\n\nimpl FromStr for StructType {\n    type Err = anyhow::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        if s == \"record\" {\n            return Ok(StructType::unnamed(Vec::new()));\n        }\n        if !(s.starts_with(\"struct<\") && s.ends_with('>')) {\n            return Err(anyhow!(\"expect struct<...>\"));\n        };\n        let mut fields = Vec::new();\n        for field in s[7..s.len() - 1].split(',') {\n            let field = field.trim();\n            let mut iter = field.split_whitespace();\n            let field_name = iter.next().unwrap().to_owned();\n            let field_type = DataType::from_str(iter.next().unwrap())?;\n            fields.push((field_name, field_type));\n        }\n        Ok(Self::new(fields))\n    }\n}\n","sourceCodeStart":253,"sourceCodeEnd":284,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/common/src/types/struct_type.rs#L253-L284","documentation":"StructType::from_str parses textual struct types like `struct<a int,b int>` (and `record` for the empty unnamed struct). If the string does not start with `struct<` and end with `>`, the parser rejects it with this error.","triggerScenarios":"Calling `s.parse::<StructType>()` with strings like `struct(a int)`, `row(...)`, a plain field list, or `record<...>` instead of `struct<...>`.","commonSituations":"Migrating type names from other systems (Spark `struct<...>` works; Flink `ROW<...>` does not), hand-edited catalog entries, or strings that lost the wrapper during serialization.","solutions":["Format the string as `struct<field1 type1,field2 type2>` or exactly `record` for empty structs","Convert ROW<...>/STRUCT(...)-style names to the `struct<...>` form before parsing","Trim whitespace around the whole string"],"exampleFix":"// before\nlet ty: StructType = \"struct(a int)\".parse()?;\n// after\nlet ty: StructType = \"struct<a int>\".parse()?;","handlingStrategy":"validation","validationCode":"fn is_struct_type_string(s: &str) -> bool {\n    let s = s.trim();\n    s == \"record\" || (s.starts_with(\"struct<\") && s.ends_with('>'))\n}","typeGuard":"fn looks_like_struct_type(s: &str) -> bool {\n    let s = s.trim();\n    s == \"record\" || (s.starts_with(\"struct<\") && s.ends_with('>'))\n}","tryCatchPattern":"let ty = s.parse::<StructType>()\n    .with_context(|| format!(\"invalid struct type string: {s}\"))?;","preventionTips":["Generate struct type strings via StructType's Display","Convert ROW<...>/STRUCT(...) dialects to struct<...> first","Handle the special-case string \"record\" for empty structs"],"tags":["rust","type-parsing","struct-type"],"backgroundTag":"invalid-argument-format","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}