herdrdev/herdr · error · io::Error
failed to create private herdr ssh config directory
Error message
failed to create private herdr ssh config directory
What it means
create_remote_ssh_config_dir walks candidate parent directories trying to create herdr's private SSH config dir with mode 0700 (src/platform/unix_common.rs:50-76). If a candidate exists but is not a usable directory the loop skips it, and when no candidate succeeds the function returns AlreadyExists with 'failed to create private herdr ssh config directory'. It is a fallback path used when the preferred per-SSH-host config location is unavailable.
Source
Thrown at src/ipc.rs:95
Ok(listener)
}
}
pub(crate) fn prepare_socket_path(
path: &Path,
busy_message: impl FnOnce(&Path) -> String,
) -> io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
if !path.exists() {
return Ok(());
}
match connect_local_stream(path) {
Ok(_) => {
return Err(io::Error::new(io::ErrorKind::AddrInUse, busy_message(path)));
}
Err(err) if stale_socket_connect_error(err.kind()) => {}
Err(err) => return Err(err),
}
if let Err(err) = fs::remove_file(path) {
if err.kind() != io::ErrorKind::NotFound {
return Err(err);
}
}
Ok(())
}
fn stale_socket_connect_error(kind: io::ErrorKind) -> bool {
matches!(
kind,
io::ErrorKind::ConnectionRefused | io::ErrorKind::NotFound | io::ErrorKind::TimedOutView on GitHub (pinned to f457cff4f2)
Solutions
- Inspect the candidate paths (under ~/.ssh) and remove or rename any regular file occupying the expected herdr config dir name
- Fix permissions on the parent directory (chmod u+rwx ~/.ssh) or clear root-owned leftovers
- Unset stray SSH-related env overrides (SSH_HOME/HERDR_* socket overrides) and retry
- Retry after fixing $HOME or the filesystem (e.g. read-only mount)
Example fix
# before: a file blocks the private config dir ls -la ~/.ssh/herdr* # shows a regular file # after rm ~/.ssh/herdr-ssh-config # then retry the ssh session command
Defensive patterns
Strategy: validation
Validate before calling
for candidate in candidate_parents() {
let dir = candidate.join(HERDR_SSH_DIR_NAME);
if dir.is_file() { /* conflict: remove or bail with a clear error */ }
} Try / catch
match create_remote_ssh_config_dir(&path) {
Err(e) if e.kind() == io::ErrorKind::AlreadyExists =>
cleanup_conflicting_entries_and_retry(&path),
r => r,
} Prevention
- Ensure ~/.ssh is user-owned and writable
- Clean up leftover files from older herdr versions at the private config dir path
When it happens
Trigger: Preparing a remote SSH control/config path on Unix where every candidate parent already contains a conflicting non-directory entry named the same as the private herdr ssh config dir, or where creation otherwise fails with an error other than success/AlreadyExists-skip.
Common situations: Stale files left at the expected directory path by older herdr versions, read-only or permission-restricted parent dirs ($HOME/.ssh locked down, root-owned leftovers), or containers with unusual HOME layouts.
Related errors
- failed to create private herdr ssh config directory
- failed to create private herdr ssh config directory
- failed to create unique scrollback temp file
- failed to {operation} managed plugin checkout at {}; close a
- failed to inspect {REMOTE_BINARY_ENV_VAR} path {}: {err}
AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28).
Data as JSON: /api/errors/e219b3902495f6b6.
Report an issue: GitHub.