{"record":{"id":"e219b3902495f6b6","repo":"herdrdev/herdr","slug":"failed-to-create-private-herdr-ssh-config-director","errorCode":null,"errorMessage":"failed to create private herdr ssh config directory","messagePattern":"failed to create private herdr ssh config directory","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/ipc.rs","lineNumber":95,"sourceCode":"        Ok(listener)\n    }\n}\n\npub(crate) fn prepare_socket_path(\n    path: &Path,\n    busy_message: impl FnOnce(&Path) -> String,\n) -> io::Result<()> {\n    if let Some(parent) = path.parent() {\n        fs::create_dir_all(parent)?;\n    }\n\n    if !path.exists() {\n        return Ok(());\n    }\n\n    match connect_local_stream(path) {\n        Ok(_) => {\n            return Err(io::Error::new(io::ErrorKind::AddrInUse, busy_message(path)));\n        }\n        Err(err) if stale_socket_connect_error(err.kind()) => {}\n        Err(err) => return Err(err),\n    }\n\n    if let Err(err) = fs::remove_file(path) {\n        if err.kind() != io::ErrorKind::NotFound {\n            return Err(err);\n        }\n    }\n\n    Ok(())\n}\n\nfn stale_socket_connect_error(kind: io::ErrorKind) -> bool {\n    matches!(\n        kind,\n        io::ErrorKind::ConnectionRefused | io::ErrorKind::NotFound | io::ErrorKind::TimedOut","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/herdrdev/herdr/blob/f457cff4f2648eee85d176f8a41861241d4e8428/src/ipc.rs#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before: a file blocks the private config dir\nls -la ~/.ssh/herdr*  # shows a regular file\n\n# after\nrm ~/.ssh/herdr-ssh-config  # then retry the ssh session command","handlingStrategy":"validation","validationCode":"for candidate in candidate_parents() {\n    let dir = candidate.join(HERDR_SSH_DIR_NAME);\n    if dir.is_file() { /* conflict: remove or bail with a clear error */ }\n}","typeGuard":null,"tryCatchPattern":"match create_remote_ssh_config_dir(&path) {\n    Err(e) if e.kind() == io::ErrorKind::AlreadyExists =>\n        cleanup_conflicting_entries_and_retry(&path),\n    r => r,\n}","preventionTips":["Ensure ~/.ssh is user-owned and writable","Clean up leftover files from older herdr versions at the private config dir path"],"tags":["ssh","filesystem","permissions","unix","rust"],"backgroundTag":"directory-creation-conflict","analyzedSha":"f457cff4f2648eee85d176f8a41861241d4e8428","analyzedAt":"2026-08-28T15:41:09.197Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}