windmill-labs/windmill · error

Supplied schema draft version is unsuported

Error message

Supplied schema draft version is unsuported

What it means

Schema::from_schema only supports JSON Schema draft 2020-12 and requires the `$schema` keyword to say exactly "https://json-schema.org/draft/2020-12/schema". Any other declared draft (draft-07, 2019-09, http vs https, trailing differences) triggers this error. Note the message contains a typo ("unsuported").

Source

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

                let parsed_val = Value::from_str(raw_val.get()).map_err(|e| {
                    Error::ArgumentErr(format!("Failed to parse `{key}` argument: {e}"))
                })?;
                for rule in rules {
                    rule.apply_rule(key, &parsed_val, self.required.contains(key))?;
                }
            }
        }

        Ok(())
    }

    pub fn from_schema(schema: &str) -> Result<Self, Error> {
        let schema: Value = serde_json::from_str(schema)?;

        if let Some(draft_version) = schema.get("$schema") {
            match draft_version.as_str() {
                Some("https://json-schema.org/draft/2020-12/schema") => (),
                _ => return Err(anyhow!("Supplied schema draft version is unsuported").into()),
            }
        } else {
            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>>()?;

View on GitHub (pinned to e474e8803c)

Solutions

  1. Set "$schema": "https://json-schema.org/draft/2020-12/schema" at the schema root.
  2. If the schema uses draft-07 constructs, port them to 2020-12 (usually a direct match for basic type/required/properties/enum).
  3. Check for exact-string issues: use https (not http) and no trailing '#' variants of a different URL.

Example fix

// before
{"$schema": "http://json-schema.org/draft-07/schema#", ...}
// after
{"$schema": "https://json-schema.org/draft/2020-12/schema", ...}
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED_DRAFT: &str = "https://json-schema.org/draft/2020-12/schema";
fn validate_draft(schema: &serde_json::Value) -> Result<(), String> {
    match schema.get("$schema").and_then(|v| v.as_str()) {
        Some(d) if d == SUPPORTED_DRAFT => Ok(()),
        Some(d) => Err(format!("unsupported draft {d}; use {SUPPORTED_DRAFT}")),
        None => Err("missing $schema".into()),
    }
}

Type guard

fn is_supported_draft(schema: &serde_json::Value) -> bool {
    schema.get("$schema").and_then(|v| v.as_str())
        == Some("https://json-schema.org/draft/2020-12/schema")
}

Try / catch

match Schema::from_schema(&schema_str) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("draft version") => {
        eprintln!("convert the schema to draft 2020-12 and set $schema accordingly: {e}");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing a schema string to from_schema whose `$schema` is e.g. "http://json-schema.org/draft-07/schema#", "https://json-schema.org/draft/2019-09/schema", or a URL with different casing/format.

Common situations: Reusing schemas written for draft-07 (still the most common in the wild), schemas exported by editors defaulting to draft-04/07, and old http:// URLs instead of the https:// 2020-12 canonical URL.

Related errors


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