windmill-labs/windmill · error

Failed to parse code

Error message

Failed to parse code

What it means

parse_csharp_sig_meta parses C# source with tree-sitter to find the Main method signature and derive the script kind. parser.parse(code, None) returns a tree that is unwrapped with .expect("Failed to parse code"), which panics when the tree-sitter parse produces no tree (allocation/parse failure). Despite the message wording, most malformed C# still yields a tree — this expect fires on hard parse failures, and downstream find_main_signature returns None for code without Main.

Source

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

    pub class_name: Option<String>,
    pub main_sig: MainArgSignature,
}

fn csharp_param_default_value<'a>(def: Node<'a>, code: &str) -> Option<serde_json::Value> {
    def.utf8_text(code.as_bytes())
        .ok()
        .and_then(|content| serde_json::from_str(content).ok())
}

pub fn parse_csharp_sig_meta(code: &str) -> anyhow::Result<CsharpMainSigMeta> {
    let mut parser = tree_sitter::Parser::new();
    let language = tree_sitter_c_sharp::LANGUAGE;
    parser
        .set_language(&language.into())
        .map_err(|e| anyhow!("Error setting c# as language: {e}"))?;

    // Parse code
    let tree = parser.parse(code, None).expect("Failed to parse code");
    let root_node = tree.root_node();

    // Traverse the AST to find the Main method signature
    let main_sig = find_main_signature(root_node, code);
    let auto_kind = if main_sig.is_none() {
        Some("lib".to_string())
    } else {
        None
    };
    let mut is_async = false;
    let mut is_public = false;
    let mut returns_void = false;
    let mut class_name = None;

    let mut args = vec![];
    if let Some((sig, name)) = main_sig {
        class_name = name;
        for sig_node in sig.children(&mut sig.walk()) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Validate the C# source compiles/parses locally (dotnet build or a C# linter) before uploading.
  2. Ensure the script content is complete, UTF-8 text and non-empty.
  3. If parsing valid code, pin/update tree-sitter-c-sharp to a version matching the tree-sitter runtime in Cargo.lock.
  4. In code, replace the expect with graceful error handling so malformed scripts report a user-facing validation error instead of panicking.

Example fix

// before
let tree = parser.parse(code, None).expect("Failed to parse code");
// after
let tree = parser
    .parse(code, None)
    .ok_or_else(|| anyhow!("Failed to parse code"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the C# source is non-empty, UTF-8 text before calling the parser
fn is_plausible_csharp(code: &str) -> bool {
    !code.trim().is_empty() && code.is_char_boundary(0)
}

Try / catch

// The call panics via expect, so run it on an isolated thread if callers must survive:
let result = std::panic::catch_unwind(|| parse_csharp_sig_meta(code));
match result {
    Ok(Ok(meta)) => meta,
    _ => return Err(anyhow!("C# script could not be parsed; check syntax")),
}

Prevention

When it happens

Trigger: Calling parse_csharp_signature/parse_csharp_sig_meta (script upload/analysis path for csharp scripts) where tree_sitter cannot produce a parse tree for the supplied code.

Common situations: Uploading a C# script with severely malformed/truncated source, non-UTF8 or binary content, or an empty payload; a tree-sitter-c-sharp grammar/runtime mismatch after dependency updates.

Understand the failure class

Related errors


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