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
- Set "$schema": "https://json-schema.org/draft/2020-12/schema" at the schema root.
- If the schema uses draft-07 constructs, port them to 2020-12 (usually a direct match for basic type/required/properties/enum).
- 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
- Pin every schema to "$schema": "https://json-schema.org/draft/2020-12/schema"
- Migrate draft-07 schemas on ingestion; most basic keywords map 1:1
- Compare the $schema URL exactly — http vs https and trailing fragments matter
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
- Unsupported value for type field, expected string or string
- enum variants are not in an array
- No draft version supplied
- Missing `required` field on schema
- `required` field should be an array of strings
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/809fd09211f6586b.
Report an issue: GitHub.