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
- 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
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
- 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
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
- failed to open log stream: {}: {}
- {} has no parent?
- config file path is not UTF-8
- path entry {} is not representable as utf8
- Domain::spawn requires that the cwd be unicode in {:?}
AI-assisted analysis of wezterm/wezterm@3ff7522b96 (2026-08-20).
Data as JSON: /api/errors/3595c638943c2355.
Report an issue: GitHub.