windmill-labs/windmill · error

Missing `properties` field on schema

Error message

Missing `properties` field on schema

What it means

from_schema requires a top-level `properties` object mapping property names to their sub-schemas. A schema with no `properties` key is rejected, even if empty properties would be semantically valid — an explicit "properties": {} is needed.

Source

Thrown at backend/windmill-common/src/schema.rs:539

            return Err(anyhow!("No draft version supplied").into());
        }

        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 {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Add a top-level "properties" object with the property definitions (use {} if there are none).
  2. If the root value is not an object, this API is not the right fit — from_schema validates object-shaped arguments only.

Example fix

// before
{"$schema": "https://json-schema.org/draft/2020-12/schema", "required": []}
// after
{"$schema": "https://json-schema.org/draft/2020-12/schema", "properties": {}, "required": []}
Defensive patterns

Strategy: validation

Validate before calling

fn require_properties_field(schema: &serde_json::Value) -> Result<(), String> {
    if schema.get("properties").is_none() {
        Err("add top-level `properties` object (use {} if empty)".into())
    } else { Ok(()) }
}

Type guard

fn has_properties_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("Missing `properties` field") => {
        eprintln!("add a top-level `properties` object before calling from_schema");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling Schema::from_schema with a schema that lacks the top-level `properties` key, e.g. only $schema/type/required are present, or when properties were accidentally nested under a different key.

Common situations: Minimal or malformed schemas, schemas intended for array/primitive roots (which this API doesn't model — it validates objects), and schemas built dynamically where the properties step was skipped.

Related errors


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