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

On fallback platforms, Herdr creates a private directory (~/.ssh-like, mode 700) for remote SSH control sockets and config. When every candidate private directory fails to create for a non-AlreadyExists reason and all paths are exhausted, this AlreadyExists error is returned as a terminal marker meaning 'no usable private directory could be established'.

Source

Thrown at src/platform/fallback.rs:35

    super::RemoteSshConfigPaths {
        user_config: std::env::var_os("HOME")
            .map(PathBuf::from)
            .map(|home| home.join(".ssh").join("config")),
        system_config: None,
        multiplexing: false,
    }
}

pub(crate) fn create_remote_ssh_config_dir(_control_socket_name: &str) -> std::io::Result<PathBuf> {
    for attempt in 0..100 {
        let dir = std::env::temp_dir().join(format!("herdr-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> {
    let mut options = std::fs::OpenOptions::new();
    options.write(true).create_new(true);
    #[cfg(unix)]
    {
        use std::os::unix::fs::OpenOptionsExt;
        options.mode(0o600);
    }
    options.open(path)
}

View on GitHub (pinned to f457cff4f2)

Solutions

  1. Check permissions on your home and SSH config directories; ensure the process can create mode-700 directories
  2. Ensure HOME is set to a writable directory for the Herdr process
  3. Pre-create the expected private herdr ssh directory with mode 700 so creation succeeds immediately
  4. If running in a sandbox/container, mount or redirect HOME to writable storage

Example fix

// before: $HOME is read-only, creation fails
// after: pre-create with correct perms
mkdir -p ~/.local/share/herdr/ssh && chmod 700 ~/.local/share/herdr/ssh
Defensive patterns

Strategy: fallback

Validate before calling

let dir = std::env::home_dir().unwrap_or_default().join(".ssh");
if !dir.is_dir() && std::fs::create_dir_all(&dir).is_err() {
    // surface a permissions problem before calling create_remote_ssh_config_dir
}

Try / catch

Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists && e.to_string().contains("private herdr ssh config") => {
    // treat as 'no usable private dir': prompt user or use a temp dir fallback
}

Prevention

When it happens

Trigger: Calling create_remote_ssh_config_dir on a fallback platform when all candidate directories fail creation (permission denied on the parent, read-only home) or none of the attempted creations succeed. The AlreadyExists kind is a sentinel: candidates whose directory already exists are skipped, and reaching the end without success yields this error.

Common situations: Home directory permissions lock down creation of the private dir; HOME unset or pointing somewhere unwritable; sandboxed/containerized runs with a read-only home; tests that exhaust candidate paths deliberately.

Related errors


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