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

Windows implementation of create_remote_ssh_config_dir mirrors the fallback logic: it tries candidate private directories, skips ones that already exist, and if every candidate fails for a real reason (permissions, read-only disk) it returns this AlreadyExists error as a terminal 'no usable directory' marker rather than the underlying IO error.

Source

Thrown at src/platform/windows.rs:135

        system_config: std::env::var_os("PROGRAMDATA")
            .map(PathBuf::from)
            .map(|dir| dir.join("ssh").join("ssh_config")),
        multiplexing: false,
    }
}

pub(crate) fn create_remote_ssh_config_dir(_control_socket_name: &str) -> std::io::Result<PathBuf> {
    let base = remote_private_temp_base();
    std::fs::create_dir_all(&base)?;
    for attempt in 0..100 {
        let dir = base.join(format!("ssh-{}-{attempt}", std::process::id()));
        match create_remote_private_dir(&dir) {
            Ok(()) => return Ok(dir),
            Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => continue,
            Err(err) => return Err(err),
        }
    }
    Err(std::io::Error::new(
        std::io::ErrorKind::AlreadyExists,
        "failed to create private herdr ssh config directory",
    ))
}

pub(crate) fn create_remote_ssh_config_file(
    path: &std::path::Path,
) -> std::io::Result<std::fs::File> {
    std::fs::OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(path)
}

pub(crate) fn create_remote_private_dir(path: &std::path::Path) -> std::io::Result<()> {
    use interprocess::os::windows::security_descriptor::{
        AsSecurityDescriptorExt as _, SecurityDescriptor,
    };

View on GitHub (pinned to f457cff4f2)

Solutions

  1. Check ACLs on %USERPROFILE% and any herdr data directories; grant the process create-directory rights
  2. Ensure %USERPROFILE% (or the configured home) is on a writable volume
  3. Pre-create the expected herdr ssh config directory manually so creation is unnecessary
  4. If AV/group policy is blocking, add an exclusion for the Herdr data directory
Defensive patterns

Strategy: fallback

Validate before calling

let base = std::env::var_os("USERPROFILE").map(std::path::PathBuf::from);
if let Some(base) = base {
    let meta = std::fs::metadata(&base);
    if meta.is_err() || meta?.permissions().readonly() {
        // surface unwritable-profile problem before calling
    }
}

Try / catch

Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists && e.to_string().contains("private herdr ssh config") => {
    // no usable dir: offer fallback to %TEMP%-based dir or prompt for elevation
}

Prevention

When it happens

Trigger: Calling create_remote_ssh_config_dir on Windows when create_remote_private_dir fails on every candidate directory with a non-AlreadyExists error — e.g. ACCESS_DENIED on the parent folder, read-only volume, or antivirus blocking directory creation.

Common situations: UserProfile redirected to a locked-down or roaming profile directory; corporate policy or AV intercepting creation of dot-directories; running Herdr from a service account whose profile path is unwritable.

Related errors


AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28). Data as JSON: /api/errors/bb253f5cb0d8fb8a. Report an issue: GitHub.