windmill-labs/windmill · error

WINDMILL_DIR must not end with a trailing slash, got: {dir}

Error message

WINDMILL_DIR must not end with a trailing slash, got: {dir}

What it means

After resolving WINDMILL_DIR (env var or normalized temp dir), Windmill validates its shape because it later appends paths like `{dir}/windmill`, `{dir}/logs`, `{dir}/cache/`. A trailing slash would yield double slashes and break assumptions about the path format, so the process panics at initialization if WINDMILL_DIR ends with '/' or '\\'.

Source

Thrown at backend/windmill-common/src/worker.rs:834

lazy_static::lazy_static! {
    pub static ref WINDMILL_DIR: String = {
        let dir = std::env::var("WINDMILL_DIR")
            .unwrap_or_else(|_| {
                #[cfg(not(windows))]
                { "/tmp/windmill".to_string() }
                #[cfg(windows)]
                {
                    let temp = std::env::temp_dir();
                    let temp_str = temp.to_string_lossy();
                    let normalized = temp_str.trim_end_matches(&['/', '\\'][..]).replace('\\', "/");
                    format!("{}/windmill", normalized)
                }
            });
        if dir.is_empty() {
            panic!("WINDMILL_DIR must not be empty");
        }
        if dir.ends_with('/') || dir.ends_with('\\') {
            panic!("WINDMILL_DIR must not end with a trailing slash, got: {dir}");
        }
        dir
    };
    pub static ref TMP_LOGS_DIR: String = format!("{}/logs", *WINDMILL_DIR);
    pub static ref ROOT_CACHE_DIR: String = format!("{}/cache/", *WINDMILL_DIR);
    pub static ref HUB_CACHE_DIR: String = format!("{}hub", *ROOT_CACHE_DIR);
    pub static ref HUB_RT_CACHE_DIR: String = format!("{}hub_rt", *ROOT_CACHE_DIR);
}

pub fn write_file(dir: &str, path: &str, content: &str) -> error::Result<File> {
    let path = format!("{}/{}", dir, path);
    let mut file = File::create(&path).map_err(|e| {
        tracing::error!("Failed to create file at {path}: {:?}", &e);
        e
    })?;
    file.write_all(content.as_bytes())?;
    file.flush()?;
    Ok(file)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Remove the trailing slash from the WINDMILL_DIR value (e.g. /tmp/windmill instead of /tmp/windmill/)
  2. Sanitize the value in your Helm chart / config template before injecting it
  3. Note that the temp-dir default path already normalizes this for you — unset the var if you don't need a custom dir

Example fix

// before
WINDMILL_DIR=/tmp/windmill/
// after
WINDMILL_DIR=/tmp/windmill
Defensive patterns

Strategy: validation

Validate before calling

const dir = process.env.WINDMILL_DIR;
if (dir && /[\\/]$/.test(dir)) throw new Error(`WINDMILL_DIR must not end with a slash: ${dir}`);

Prevention

When it happens

Trigger: Setting `WINDMILL_DIR=/tmp/windmill/` (trailing slash) or a Windows path ending in a backslash in the environment of any Windmill binary.

Common situations: Copy-pasting a path with a trailing slash from a file manager; templating `WINDMILL_DIR: "{{ .Values.dir }}/"`; Windows bind mount paths like `C:\\windmill\\`.

Related errors


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