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

  1. Fix the parameter list so each comma-separated segment is a valid identifier: `def main [a: int, b?: string]`.
  2. Remove trailing/leading or doubled commas in the `def main [...]` signature.
  3. Check for invisible characters (zero-width spaces) that break identifier extraction; retype the signature plainly.
  4. 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

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


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