windmill-labs/windmill · error
WINDMILL_DIR must not be empty
Error message
WINDMILL_DIR must not be empty
What it means
Windmill derives all local storage paths (job dirs, cache, logs) from the WINDMILL_DIR environment variable (defaulting to a normalized temp dir when unset). At lazy-static initialization it validates the resolved value; if it resolves to an empty string the process panics immediately, since an empty root would produce invalid paths like '/logs'.
Source
Thrown at backend/windmill-common/src/worker.rs:831
WORKER_PULL_QUERIES_FAIRNESS.store(std::sync::Arc::new(fairness_queries));
}
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
})?;View on GitHub (pinned to e474e8803c)
Solutions
- Set WINDMILL_DIR to a non-empty absolute path (e.g. /tmp/windmill) or unset the variable entirely to use the temp-dir default
- Check for empty values injected by your orchestrator (k8s env, compose, systemd EnvironmentFile)
- Ensure your entrypoint script does not export an empty variable from an unset prerequisite
Example fix
// before docker run -e WINDMILL_DIR= ... // after docker run -e WINDMILL_DIR=/tmp/windmill ...
Defensive patterns
Strategy: validation
Validate before calling
const v = process.env.WINDMILL_DIR;
if (v === '') throw new Error('WINDMILL_DIR is set but empty; unset it or give it a value'); Type guard
function hasWindmillDir(env) {
return typeof env.WINDMILL_DIR === 'string' && env.WINDMILL_DIR.length > 0;
} Prevention
- Never set WINDMILL_DIR to an empty string in compose/k8s env blocks — omit the key instead
- Validate rendered env files for empty values in CI
- Audit secret/config injectors that may export empty strings
When it happens
Trigger: Setting `WINDMILL_DIR=` (empty value) in the environment of a server/worker/indexer process; some orchestrators or secret injectors export the variable with an empty string.
Common situations: Kubernetes ConfigMap/secret with an empty value for the key; docker-compose `WINDMILL_DIR: ""`; shell script doing `export WINDMILL_DIR=$UNSET_VAR` where the var is empty.
Related errors
- BASE_INTERNAL_URL is required in agent mode
- WINDMILL_DIR must not end with a trailing slash, got: {dir}
- Server mode requires a database connection
- Agent mode is only available in the EE, ignoring...
- Indexer mode requires compiling with the tantivy feature fla
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/7a270488a4039f63.
Report an issue: GitHub.