windmill-labs/windmill · error
could not create dir '{directory_path}': {e}
Error message
could not create dir '{directory_path}': {e} What it means
Windmill workers and servers create local directories (job working dirs, caches, logs) under WINDMILL_DIR on startup or when handling jobs. `create_directory_async` builds the directory recursively (like `mkdir -p`) and panics with this message if the OS refuses, e.g. due to permissions, a read-only filesystem, or a path component being a file. The panic kills the calling task (worker startup or job run).
Source
Thrown at backend/windmill-common/src/utils.rs:503
(per_page, offset)
}
pub async fn now_from_db<'c, E: sqlx::PgExecutor<'c>>(
db: E,
) -> Result<chrono::DateTime<chrono::Utc>> {
Ok(sqlx::query_scalar!("SELECT now()")
.fetch_one(db)
.warn_after_seconds_with_sql(1, "now_from_db".to_string())
.await?
.unwrap())
}
pub async fn create_directory_async(directory_path: &str) {
AsyncDirBuilder::new()
.recursive(true)
.create(directory_path)
.await
.unwrap_or_else(|e| panic!("could not create dir '{}': {}", directory_path, e));
}
pub fn create_directory_sync(directory_path: &str) {
SyncDirBuilder::new()
.recursive(true)
.create(directory_path)
.unwrap_or_else(|e| panic!("could not create dir '{}': {}", directory_path, e));
}
#[track_caller]
pub fn not_found_if_none<T, U: AsRef<str>>(opt: Option<T>, kind: &str, name: U) -> Result<T> {
if let Some(o) = opt {
Ok(o)
} else {
let loc = Location::caller();
Err(Error::NotFound(format!(
"{} not found at name {} ({}:{})",
kind,View on GitHub (pinned to e474e8803c)
Solutions
- Check the inner error `{e}` in the message for the exact OS errno (permission denied, read-only fs, etc.)
- Verify WINDMILL_DIR points to a writable volume and is owned by the user the worker runs as
- Ensure the mount is not read-only (`mount | grep <path>` / volume ro flags in compose/k8s)
- Remove any regular file that occupies a path component of the target directory
- Run the container with the correct fsGroup/runAsUser so the volume is writable
Example fix
// before: worker running as user 1000 with root-owned volume volumes: - worker-data:/tmp/windmill // after: make volume writable volumes: - worker-data:/tmp/windmill # plus in k8s: securityContext.fsGroup: 1000 or chown 1000:1000 on the host dir
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function assertWritableDirParent(dir) {
let p = require('path').dirname(dir);
while (p !== '/' && !fs.existsSync(p)) p = require('path').dirname(p);
fs.accessSync(p, fs.constants.W_OK); // throws if not writable
}
assertWritableDirParent(process.env.WINDMILL_DIR || '/tmp/windmill'); Try / catch
// Wrap any custom automation that creates Windmill dirs
try {
await fs.promises.mkdir(target, { recursive: true });
} catch (e) {
if (e.code === 'EACCES' || e.code === 'EROFS') {
console.error(`Fix volume permissions/read-only mount for ${target}: ${e.message}`);
}
throw e;
} Prevention
- Mount WINDMILL_DIR on a writable volume with correct ownership (match container UID/fsGroup)
- Never mount the storage path read-only
- Check for files colliding with expected directories after image upgrades
- Verify writable path in CI smoke tests before deploy
When it happens
Trigger: Calling `create_directory_async(dir)` where the async tokio directory builder returns an error: parent path is a file, mount point read-only, EACCES on the target path, disk full, or invalid characters in the path.
Common situations: WINDMILL_DIR volume mounted read-only; container running as non-root but volume owned by root; Kubernetes emptyDir/persistence misconfigured; a file exists where the directory should be; NFS/permission issues on shared storage.
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
- Failed to set permissions to {}: {e}
- failed to execute replace_ephemeral command: {}
- ENABLE_UNSHARE_PID is set but unshare test failed. Error: {}
- ENABLE_UNSHARE_PID is set but unshare binary not found. Inst
- ENABLE_UNSHARE_PID is set but failed to test unshare: {}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/fdc0f67d1a39c39f.
Report an issue: GitHub.