wezterm/wezterm · error · anyhow::Error

path {} has no parent dir!?

Error message

path {} has no parent dir!?

What it means

open_log() prepares the daemon's log file: it first creates the parent directory chain via create_user_owned_dirs, which needs path.parent(). Path::parent() returns None only when the path is empty or is the root '/' — meaning the configured log path cannot have a parent directory at all. Hence the incredulous '!?': reaching it means an empty or root path was passed, not a normal file path.

Source

Thrown at config/src/daemon.rs:38

        use std::os::unix::fs::PermissionsExt;
        if let Ok(metadata) = path.metadata() {
            let mut perms = metadata.permissions();
            let mode = perms.mode();
            perms.set_mode(mode | libc::S_ISVTX as u32);
            let _ = std::fs::set_permissions(&path, perms);
        }
    }

    #[cfg(windows)]
    {
        let _ = path;
    }
}

fn open_log(path: PathBuf) -> anyhow::Result<File> {
    create_user_owned_dirs(
        path.parent()
            .ok_or_else(|| anyhow!("path {} has no parent dir!?", path.display()))?,
    )?;
    let mut options = OpenOptions::new();
    options.write(true).create(true).append(true);
    options
        .open(&path)
        .map_err(|e| anyhow!("failed to open log stream: {}: {}", path.display(), e))
}

impl DaemonOptions {
    #[cfg_attr(windows, allow(dead_code))]
    pub fn pid_file(&self) -> PathBuf {
        self.pid_file
            .as_ref()
            .cloned()
            .unwrap_or_else(|| RUNTIME_DIR.join("pid"))
    }

    pub fn stdout(&self) -> PathBuf {

View on GitHub (pinned to 3ff7522b96)

Solutions

  1. Set an explicit file path under a directory (e.g. ~/.local/state/wezterm/out.log) or leave the option unset to use built-in defaults
  2. Default empty values to the standard location before calling: path = if path.as_os_str().is_empty() { default } else { path }
  3. Validate at config parse time: reject empty or '/' log paths with a clear message instead of failing deep in open_log
  4. Check for template/env substitution that silently produced an empty string

Example fix

// before
let file = open_log(PathBuf::from(&env::var("WEZTERM_LOG").unwrap()))?; // empty env -> 'path  has no parent dir!?'
// after
let raw = env::var("WEZTERM_LOG").unwrap_or_default();
let path = if raw.is_empty() { RUNTIME_DIR.join("out.log") } else { PathBuf::from(raw) };
let file = open_log(path)?;
Defensive patterns

Strategy: validation

Validate before calling

// reject unusable log paths up front, before the daemon starts
fn ensure_log_path(p: &Path) -> anyhow::Result<()> {
    if p.as_os_str().is_empty() || p == Path::new("/") {
        anyhow::bail!("log path must be a file path, got {:?}", p);
    }
    Ok(())
}

Prevention

When it happens

Trigger: DaemonOptions stdout/stderr (or any caller of open_log) given PathBuf::new("") or "/" as the log path — e.g. an empty string arriving from config/CLI where an env var or template slot was unset, or a path-building bug producing an empty PathBuf.

Common situations: Scripts passing an env var that is unset and becomes an empty string; config templates with conditionally-empty log paths; tests constructing DaemonOptions and invoking logging helpers with leftover empty values.

Related errors


AI-assisted analysis of wezterm/wezterm@3ff7522b96 (2026-08-20). Data as JSON: /api/errors/3595c638943c2355. Report an issue: GitHub.