windmill-labs/windmill · error

Invalid gemfile do block

Error message

Invalid gemfile do block

What it means

When extracting the gemfile body, `parse_ruby_requirements` takes the AST node text of the `do ... end` block and slices off 2 leading bytes ('do') and 3 trailing bytes ('end') to get the inner requirement lines. If the resulting byte range is invalid (block text shorter than 5 bytes, i.e. `get(2..len-3)` returns None), it raises "Invalid gemfile do block".

Source

Thrown at backend/parsers/windmill-parser-ruby/src/lib.rs:100

                    lazy_static::lazy_static! {
                        static ref WINDMILL_RE: Regex = Regex::new(r"(?m)^\s*require\s*'windmill/inline'").unwrap();
                    }

                    if WINDMILL_RE.find(&code).is_none() {
                        return Err(anyhow!(
                            "`require 'windmill/inline'` is not detected - please add `require 'windmill/inline'` in order to use inline gemfile.
                Your Gemfile syntax will be compatible with bundler/inline."
                        )
                        .into());
                    }

                    return req
                        // gemfile do_block comes with 'do' and 'end'
                        // we want to omit these by taking slice
                        .get(2..(req.len() - 3))
                        .map(str::to_owned)
                        .ok_or(anyhow!("Invalid gemfile do block"));
                }
            }
        }
    }
    Ok(String::new())
}

// Function to find the Main method's signature
fn find_main_signature<'a>(root_node: Node<'a>, code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
    let mut cursor = root_node.walk();
    'top_level: for x in root_node.children(&mut cursor) {
        if x.kind() == "method" {
            let mut args = vec![];
            for (i, m) in x.children(&mut x.walk()).skip(1).enumerate() {
                if i == 0
                    && m.kind() == "identifier"
                    && !m
                        .utf8_text(code.as_bytes())

View on GitHub (pinned to e474e8803c)

Solutions

  1. Give the gemfile block a non-empty body with at least one `gem 'name'` line
  2. Write the block across multiple lines: `gemfile do\n gem 'sinatra'\nend`
  3. If no gems are needed, delete the gemfile block entirely and use the requirements field instead

Example fix

// before
gemfile do end
// after
gemfile do
  gem 'sinatra'
end
Defensive patterns

Strategy: validation

Validate before calling

// # Ruby-side pre-check for a usable gemfile body:
// if code =~ /^\s*gemfile\s+do\s*end\b/   # empty one-line block
//   raise "gemfile block must contain at least one gem line"
// end

Try / catch

match parse_ruby_requirements(code) {
    Ok(g) => g,
    Err(e) if e.to_string().contains("Invalid gemfile do block") => {
        Err(anyhow!("your gemfile block has no extractable body: {e}"))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: A `gemfile do ... end` block whose parsed block-node text is too short to slice — e.g. `gemfile do end` (empty body) or `gemfile do end` written on one line where the do_block node text is `"do end"` (5 bytes → slice 2..2 is Some, but `"do"`/`"d\n"`-like degenerate nodes yield None); also mutated/odd ASTs where the captured node is not a normal do_block.

Common situations: Empty `gemfile do end` bodies; scripts where the gemfile block is malformed after tree-sitter parsing; scripts copied with unusual whitespace such that the do_block node text is minimal.

Related errors


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