{"record":{"id":"d5e6e1c69de3efff","repo":"windmill-labs/windmill","slug":"properties-field-should-be-an-object","errorCode":null,"errorMessage":"`properties` field should be an object","messagePattern":"`properties` field should be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-common/src/schema.rs","lineNumber":541,"sourceCode":"\n        let required: Vec<String> = schema\n            .get(\"required\")\n            .ok_or(anyhow!(\"Missing `required` field on schema\"))?\n            .as_array()\n            .ok_or(anyhow!(\"`required` field should be an array of strings\"))?\n            .into_iter()\n            .map(|v| {\n                v.as_str()\n                    .map(|s| s.to_string())\n                    .ok_or(anyhow!(\"required field key is not a string\"))\n            })\n            .collect::<Result<Vec<String>, anyhow::Error>>()?;\n\n        let properties = schema\n            .get(\"properties\")\n            .ok_or(anyhow!(\"Missing `properties` field on schema\"))?\n            .as_object()\n            .ok_or(anyhow!(\"`properties` field should be an object\"))?;\n\n        let mut rules = vec![];\n\n        for (key, val) in properties {\n            rules.push((\n                key.clone(),\n                SchemaValidationRule::from_value(val)\n                    .map_err(|e| anyhow!(\"Problem making rule for {key}: {e}\"))?,\n            ));\n        }\n\n        Ok(Self { required, rules })\n    }\n}\n\nimpl JsonPrimitiveType {\n    fn from_str(typ: &str) -> Result<Self, anyhow::Error> {\n        match typ {","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-common/src/schema.rs#L523-L559","documentation":"Raised by from_schema when the schema's `properties` key is missing or is not a JSON object. Windmill's restricted schema format derives per-property validation rules from `properties`, so anything else cannot be compiled into rules and the schema is rejected at parse time.","triggerScenarios":"Calling Schema::from_schema with \"properties\": [\"a\",\"b\"] (an array of names) or \"properties\": \"...\" — any non-object value at the `properties` key.","commonSituations":"Confusing a list of field names with a property map, generators emitting an array of {name, schema} pairs instead of a keyed object, and hand-edited JSON that collapsed the object.","solutions":["Convert `properties` to an object keyed by property name: \"properties\": {\"a\": {\"type\": \"string\"}}.","If you have an array of {name, schema} entries, transform it into a keyed object before passing the schema.","Use \"properties\": {} if the object genuinely has no properties."],"exampleFix":"// before\n\"properties\": [\"a\", \"b\"]\n// after\n\"properties\": {\"a\": {\"type\": \"string\"}, \"b\": {\"type\": \"number\"}}","handlingStrategy":"type-guard","validationCode":"fn validate_properties_is_object(schema: &serde_json::Value) -> Result<(), String> {\n    match schema.get(\"properties\") {\n        Some(p) if p.is_object() => Ok(()),\n        Some(_) => Err(\"`properties` must be an object keyed by property name\".into()),\n        None => Err(\"missing `properties`\".into()),\n    }\n}","typeGuard":"fn properties_is_object(schema: &serde_json::Value) -> bool {\n    schema.get(\"properties\").map(|p| p.is_object()).unwrap_or(false)\n}","tryCatchPattern":"match Schema::from_schema(&schema_str) {\n    Ok(s) => s,\n    Err(e) if e.to_string().contains(\"properties` field should be an object\") => {\n        eprintln!(\"convert `properties` from a list into a name->schema object\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Key properties by field name; don't pass arrays of field names or {name,schema} pairs","Convert array-of-definitions formats into a keyed object before serialization"],"tags":["json-schema","schema-validation","type-mismatch","windmill"],"backgroundTag":"invalid-json-schema","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}