{"record":{"id":"3595c638943c2355","repo":"wezterm/wezterm","slug":"path-has-no-parent-dir","errorCode":null,"errorMessage":"path {} has no parent dir!?","messagePattern":"path (.+?) has no parent dir!\\?","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"config/src/daemon.rs","lineNumber":38,"sourceCode":"        use std::os::unix::fs::PermissionsExt;\n        if let Ok(metadata) = path.metadata() {\n            let mut perms = metadata.permissions();\n            let mode = perms.mode();\n            perms.set_mode(mode | libc::S_ISVTX as u32);\n            let _ = std::fs::set_permissions(&path, perms);\n        }\n    }\n\n    #[cfg(windows)]\n    {\n        let _ = path;\n    }\n}\n\nfn open_log(path: PathBuf) -> anyhow::Result<File> {\n    create_user_owned_dirs(\n        path.parent()\n            .ok_or_else(|| anyhow!(\"path {} has no parent dir!?\", path.display()))?,\n    )?;\n    let mut options = OpenOptions::new();\n    options.write(true).create(true).append(true);\n    options\n        .open(&path)\n        .map_err(|e| anyhow!(\"failed to open log stream: {}: {}\", path.display(), e))\n}\n\nimpl DaemonOptions {\n    #[cfg_attr(windows, allow(dead_code))]\n    pub fn pid_file(&self) -> PathBuf {\n        self.pid_file\n            .as_ref()\n            .cloned()\n            .unwrap_or_else(|| RUNTIME_DIR.join(\"pid\"))\n    }\n\n    pub fn stdout(&self) -> PathBuf {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/wezterm/wezterm/blob/3ff7522b9617ec920f7fbdb22b57e93d42d93ee9/config/src/daemon.rs#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Default empty values to the standard location before calling: path = if path.as_os_str().is_empty() { default } else { path }","Validate at config parse time: reject empty or '/' log paths with a clear message instead of failing deep in open_log","Check for template/env substitution that silently produced an empty string"],"exampleFix":"// before\nlet file = open_log(PathBuf::from(&env::var(\"WEZTERM_LOG\").unwrap()))?; // empty env -> 'path  has no parent dir!?'\n// after\nlet raw = env::var(\"WEZTERM_LOG\").unwrap_or_default();\nlet path = if raw.is_empty() { RUNTIME_DIR.join(\"out.log\") } else { PathBuf::from(raw) };\nlet file = open_log(path)?;","handlingStrategy":"validation","validationCode":"// reject unusable log paths up front, before the daemon starts\nfn ensure_log_path(p: &Path) -> anyhow::Result<()> {\n    if p.as_os_str().is_empty() || p == Path::new(\"/\") {\n        anyhow::bail!(\"log path must be a file path, got {:?}\", p);\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default empty/missing log paths to a standard location instead of passing them through","Never forward empty env vars into path options — substitute or reject at parse time","Unit-test DaemonOptions with empty values to catch regressions early"],"tags":["config","filesystem","path","daemon","logging"],"backgroundTag":"path-validation-failed","analyzedSha":"3ff7522b9617ec920f7fbdb22b57e93d42d93ee9","analyzedAt":"2026-08-20T04:47:31.434Z","contentChangedAt":"2026-08-20T04:47:31.434Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}