windmill-labs/windmill · error
Cannot convert default value to json value: {unparsed} err
Error message
Cannot convert default value to json
value: {unparsed}
error: {e}{} What it means
`find_main_signature` extracts a Ruby parameter's default value byte-range and coerces it to JSON by naively replacing `nil` with `null` and feeding it to `serde_json::from_str`. Ruby default expressions that are not valid JSON (symbols, method calls, unquoted string literals, Ruby hash syntax with symbol keys) fail, producing "Cannot convert default value to json" with the offending value and serde error; if the serde error mentions 'key must be a string', a NOTE about hash syntax is appended.
Source
Thrown at backend/parsers/windmill-parser-ruby/src/lib.rs:143
if a.kind() == "identifier" {
a.utf8_text(code.as_bytes()).inspect(|name| {
args.push(Arg { name: (*name).to_owned(), ..Default::default() })
})?;
}
if a.kind() == "optional_parameter" {
let mut walk = a.walk();
let mut it = a.children(&mut walk).into_iter();
match (it.next().and_then(|n| n.utf8_text(code.as_bytes()).ok()), {
// Skip `=`
it.next();
it.next().map(|n| n.range())
}) {
(Some(ident), Some(Range { start_byte, end_byte, .. })) => {
let unparsed =
&code[start_byte..end_byte].replace("nil", "null");
let default: Value = serde_json::from_str(unparsed).map_err(
|e|
anyhow!("Cannot convert default value to json\n\tvalue: {unparsed}\n\terror: {e}{}",
if e.to_string().contains("key must be a string") {
"\n\nNOTE: If you are trying to declare default hash, use following syntax:\n { \"<key>\": <value> }"
} else {
""
}
))?;
args.push(Arg {
name: ident.to_owned(),
typ: json_to_typ(&default, true),
default: Some(default),
has_default: true,
..Default::default()
});
}
_ => {
let Range { start_byte, end_byte, .. } = a.range();
bail!(
"Cannot parse optional parameter: {}",View on GitHub (pinned to e474e8803c)
Solutions
- Rewrite hash defaults in JSON syntax: `{ "key": "value" }` (this is exactly what the appended NOTE suggests)
- Use only JSON-compatible literals as defaults: numbers, JSON strings (double-quoted), true/false, null, arrays, objects
- Replace symbolic or computed defaults with a simple JSON literal, or drop the default and handle absence in code
Example fix
// before
def main(a, h = { key: "value" })
// after
def main(a, h = { "key": "value" }) Defensive patterns
Strategy: validation
Validate before calling
// # Ruby-side pre-validation of defaults (must be JSON-compatible):
// def main(a, h = { "key": "value" }) # JSON-style hash, symbol-free
// end
// # quick self-check in Ruby: JSON.parse(default.to_json) rescues nothing to fix Try / catch
match signature_result {
Ok(m) => m,
Err(e) if e.to_string().contains("Cannot convert default value to json") => {
// message already embeds the value and a hash-syntax hint; pass through to user
Err(e)
}
Err(e) => Err(e),
} Prevention
- Use only JSON literals as Ruby main-signature defaults: numbers, "strings", true/false, null, [], {}
- Write hash defaults with quoted string keys: { "key": value }, never { key: value }
- Avoid symbols, method calls, and interpolated strings as defaults
When it happens
Trigger: Declaring a Ruby script main-signature argument whose default is anything but a JSON literal: `def main(a: :sym)`, `def main(x: "hello"[unquoted variant])`, `def main(h: { key: 1 })` (Ruby symbol keys), `def main(t: Time.now)`, or string defaults written without JSON-compatible quoting.
Common situations: Ruby hash defaults using `{ key: value }` symbol-key syntax instead of JSON `{ "key": value }`; boolean/default casing is fine but symbols, interpolated strings, and expressions are not; users porting Ruby defaults from idiomatic Ruby code.
Related errors
- Failed to parse flow value: {}
- {}
- Unable to deserialize group names
- bundle failed:\n${attempt.output}
- bundle produced no javascript:\n${buildOutput}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/e47aaffff367553d.
Report an issue: GitHub.