rathole-org/rathole · error
config file should have a parent dir
Error message
config file should have a parent dir
What it means
config_watcher needs the config file's parent directory to register filesystem notifications. After resolving relative paths against the current directory, the code calls path.parent() and `expect`s it to succeed; a Path without a parent (e.g. an empty or root-only path) panics with this message at src/config_watcher.rs:148.
Solutions
- Pass a valid config file path including its directory (e.g. /etc/fantastic/config.toml or ./config.toml).
- Check the environment variable or flag feeding the path isn't empty.
- Make the path absolute with a directory component before calling config_watcher.
- Default to a known-good config path when the user-supplied path is blank.
Example fix
// before
let path = std::env::var("CONFIG").unwrap(); // may be ""
// after
let path = std::env::var("CONFIG").unwrap_or_else(|_| "./config.toml".to_string()); Defensive patterns
Strategy: validation
Validate before calling
let path = std::path::PathBuf::from(config_arg);
if path.as_os_str().is_empty() || path.parent().is_none() {
anyhow::bail!("config path must be a non-empty file path with a parent directory");
} Type guard
fn has_parent(p: &std::path::Path) -> bool {
!p.as_os_str().is_empty() && p.parent().map(|x| !x.as_os_str().is_empty()).unwrap_or(false)
} Prevention
- Never pass an empty/blank config path; default it early
- Expand env vars and check for empty results
- Use absolute paths for config files
When it happens
Trigger: Calling config_watcher with a path that has no parent directory component — e.g. an empty string path, or a path that normalizes to root — so `path.parent()` returns None.
Common situations: Passing an empty --config argument; environment variable expansion that produced an empty string (e.g. CONFIG_PATH unset); constructing the path programmatically without validation.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Neither of `[server]` or `[client]` is defined
- Unknown proxy scheme
- Missing TLS configuration
- Missing `pkcs12` or `pkcs12_password`
- Try to run as a server, but the configuration is missing…
AI-assisted analysis of rathole-org/rathole@a292f7ed54 (2026-09-07).
Data as JSON: /api/errors/37d505ca01366098.
Report an issue: GitHub.
Appendix: source
Thrown at src/config_watcher.rs:148
let _ = shutdown_rx.recv().await;
Ok(())
}
#[cfg(feature = "notify")]
#[instrument(skip(shutdown_rx, event_tx, old))]
async fn config_watcher(
path: PathBuf,
mut shutdown_rx: broadcast::Receiver<bool>,
event_tx: mpsc::UnboundedSender<ConfigChange>,
mut old: Config,
) -> Result<()> {
let (fevent_tx, mut fevent_rx) = mpsc::unbounded_channel();
let path = if path.is_absolute() {
path
} else {
env::current_dir()?.join(path)
};
let parent_path = path.parent().expect("config file should have a parent dir");
let path_clone = path.clone();
let mut watcher =
notify::recommended_watcher(move |res: Result<notify::Event, _>| match res {
Ok(e) => {
if matches!(e.kind, EventKind::Modify(_))
&& e.paths
.iter()
.map(|x| x.file_name())
.any(|x| x == path_clone.file_name())
{
let _ = fevent_tx.send(true);
}
}
Err(e) => error!("watch error: {:#}", e),
})?;
watcher.watch(parent_path, RecursiveMode::NonRecursive)?;
info!("Start watching the config");View on GitHub (pinned to a292f7ed54)