windmill-labs/windmill · error
`properties` field should be an object
Error message
`properties` field should be an object
What it means
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.
Source
Thrown at backend/windmill-common/src/schema.rs:541
let required: Vec<String> = schema
.get("required")
.ok_or(anyhow!("Missing `required` field on schema"))?
.as_array()
.ok_or(anyhow!("`required` field should be an array of strings"))?
.into_iter()
.map(|v| {
v.as_str()
.map(|s| s.to_string())
.ok_or(anyhow!("required field key is not a string"))
})
.collect::<Result<Vec<String>, anyhow::Error>>()?;
let properties = schema
.get("properties")
.ok_or(anyhow!("Missing `properties` field on schema"))?
.as_object()
.ok_or(anyhow!("`properties` field should be an object"))?;
let mut rules = vec![];
for (key, val) in properties {
rules.push((
key.clone(),
SchemaValidationRule::from_value(val)
.map_err(|e| anyhow!("Problem making rule for {key}: {e}"))?,
));
}
Ok(Self { required, rules })
}
}
impl JsonPrimitiveType {
fn from_str(typ: &str) -> Result<Self, anyhow::Error> {
match typ {View on GitHub (pinned to e474e8803c)
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.
Example fix
// before
"properties": ["a", "b"]
// after
"properties": {"a": {"type": "string"}, "b": {"type": "number"}} Defensive patterns
Strategy: type-guard
Validate before calling
fn validate_properties_is_object(schema: &serde_json::Value) -> Result<(), String> {
match schema.get("properties") {
Some(p) if p.is_object() => Ok(()),
Some(_) => Err("`properties` must be an object keyed by property name".into()),
None => Err("missing `properties`".into()),
}
} Type guard
fn properties_is_object(schema: &serde_json::Value) -> bool {
schema.get("properties").map(|p| p.is_object()).unwrap_or(false)
} Try / catch
match Schema::from_schema(&schema_str) {
Ok(s) => s,
Err(e) if e.to_string().contains("properties` field should be an object") => {
eprintln!("convert `properties` from a list into a name->schema object");
return Err(e);
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: Calling Schema::from_schema with "properties": ["a","b"] (an array of names) or "properties": "..." — any non-object value at the `properties` key.
Common situations: 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.
Related errors
- `required` field should be an array of strings
- required field key is not a string
- Unsupported value for type field, expected string or string
- enum variants are not in an array
- Supplied schema draft version is unsuported
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/d5e6e1c69de3efff.
Report an issue: GitHub.