windmill-labs/windmill · error
Internal error, cannot check if argument is optional
Error message
Internal error, cannot check if argument is optional
What it means
After parsing a parameter's name, type and default, the parser checks whether the name ends with `?` to mark it optional. This fires when the extracted name string is empty so there is no last character to inspect — an internal invariant violation indicating the argument text produced an empty identifier. It signals malformed input that slipped past earlier `get`-based extraction (which used `unwrap_or("Error")`-style fallbacks elsewhere, but here yields an empty string).
Source
Thrown at backend/parsers/windmill-parser-nu/src/lib.rs:129
Some(parse_default(
&batch
.get(d..)
.ok_or(anyhow!("Cannot parse default value of argument"))?,
&batches,
i,
&mut compensate_lookahead,
)?),
)
} else {
bail!("Parsing error `:` should be before `=`\nit likely means you are trying to set default value to record or table which is not supported at the moment.")
}
}
};
// Check if it is optional
let optional = {
let Some(element) = name.chars().last() else {
bail!("Internal error, cannot check if argument is optional")
};
element == '?'
};
// Rest parameters are not supported
if matches!(name.get(0..3), Some("...")) {
bail!("Rest (...) parameters are not supported")
}
// Flags are not supported
if matches!(name.get(0..2), Some("--")) {
bail!("Flags are not supported")
}
sig.args.push(Arg {
name: if optional {
name.get(..name.len() - 1).unwrap_or("Error").to_owned()
} else {View on GitHub (pinned to e474e8803c)
Solutions
- Fix the parameter list so each comma-separated segment is a valid identifier: `def main [a: int, b?: string]`.
- Remove trailing/leading or doubled commas in the `def main [...]` signature.
- Check for invisible characters (zero-width spaces) that break identifier extraction; retype the signature plainly.
- If a valid-looking signature triggers it, report the script — it points to a parser edge case.
Example fix
// before def main [a: int, , b: string] // after def main [a: int, b: string]
Defensive patterns
Strategy: validation
Validate before calling
function validateNuParamList(sigText) {
const inner = sigText.match(/def\s+main\s*\[([^\]]*)\]/)?.[1] ?? '';
const parts = inner.split(',').map(s => s.trim()).filter(Boolean);
const bad = parts.find(p => !/^[\w-]+\??(\s*:.*)?(\s*=.*)?$/.test(p));
return bad ? `invalid parameter segment: '${bad}' — each comma-separated param must be a non-empty identifier` : null;
} Try / catch
try {
const sig = parseNuSignature(source);
} catch (e) {
if (String(e).includes('cannot check if argument is optional')) {
throw new Error('Malformed parameter list in def main: remove empty/duplicate comma segments and invisible characters');
}
throw e;
} Prevention
- Never leave empty segments between commas in the parameter list
- Retype signatures manually after copy-paste to eliminate zero-width/invisible characters
- Lint signatures with a regex for `ident[?][: type][= default]` shape before deploying
When it happens
Trigger: A parameter batch in `def main [...]` that trims to an empty string yet reaches the optional check — e.g. degenerate argument lists like `def main [,]` or inputs where splitting on commas produces an empty segment not filtered out and name extraction yields "".
Common situations: Hand-edited parameter lists with stray/empty segments or only whitespace between commas; scripts generated by tooling that emits empty args.
Related errors
- Cannot find main function.
- Parsing error `:` should be before `=` it likely means you a
- Rest (...) parameters are not supported
- Flags are not supported
- typed records are not supported, use `ident: record`
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/653500561853348a.
Report an issue: GitHub.