windmill-labs/windmill · error

Cannot find main function.

Error message

Cannot find main function.

What it means

parse_nu_signature lexes the Nushell source looking for the token sequence `def` `main` `[`...args...`]` to extract the entrypoint's parameter list. If the tokens never produce an Args state (no `def main` found, or a malformed definition), it bails. Windmill requires Nushell scripts to expose a `def main` entrypoint to infer the argument signature.

Source

Thrown at backend/parsers/windmill-parser-nu/src/lib.rs:35

        Args(String),
    }
    let mut last_token = LastToken::None;
    for token in tokens {
        let s = token.span;
        let cont = src.get(s.start..s.end).ok_or(anyhow!("Parsing error"))?;
        last_token = match last_token {
            LastToken::None if cont == "def" => LastToken::Def,
            LastToken::Def if cont == "main" => LastToken::Main,
            LastToken::Main => {
                LastToken::Args(cont.get(1..(cont.len() - 1)).unwrap_or("Error").to_owned())
            }
            LastToken::Args(_) => break,
            _ => LastToken::None,
        };
    }

    let LastToken::Args(args) = last_token else {
        bail!("Cannot find main function.");
    };

    let mut sig = MainArgSignature::default();
    sig.auto_kind = None;

    let batches = args
        .lines()
        .filter_map(|el| {
            if el.trim_start().starts_with('#') {
                None
            } else {
                Some(
                    el.split(',')
                        .map(|el| el.trim())
                        .filter(|el| el != &"")
                        .collect::<Vec<&str>>(),
                )
            }

View on GitHub (pinned to e474e8803c)

Solutions

  1. Add a `def main [ ... ] { ... }` entrypoint and move your logic/parameters into it.
  2. Keep the exact lowercase `def main` on adjacent tokens — no intervening code or unusual spacing between `def` and `main`.
  3. Name other helper functions something other than `main`, and make sure the entrypoint is literally `main`.
  4. Verify the parameter list is enclosed in square brackets `[]` right after `main`.

Example fix

// before
print "hello"
// after
def main [name: string] {
  print $"hello ($name)"
}
Defensive patterns

Strategy: validation

Validate before calling

function validateNuEntry(code) {
  if (!/^\s*def\s+main\s*\[/m.test(code)) {
    return 'Nushell scripts must define an entrypoint: def main [ ... ] { ... }';
  }
  return null;
}

Try / catch

try {
  const sig = parseNuSignature(source);
} catch (e) {
  if (String(e).includes('Cannot find main function')) {
    throw new Error('Add a `def main [args] { ... }` entrypoint; Windmill cannot infer the signature without it');
  }
  throw e;
}

Prevention

When it happens

Trigger: Deploying a Nushell script that lacks `def main []` or `def main [...]`, defines main under a different name (`def run`), or has lexing quirks (e.g. `def` and `main` split by comments/odd spacing that reset the token state machine).

Common situations: Scripts written as plain command files with top-level statements and no main wrapper; renaming main to something custom; scripts where a comment or string between `def` and `main` resets the LastToken state.

Related errors


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