{"record":{"id":"a05a1efb069dbfbc","repo":"astrid-runtime/astrid","slug":"principal-config-path-is-not-a-regular-directory","errorCode":null,"errorMessage":"principal .config path is not a regular directory","messagePattern":"principal \\.config path is not a regular directory","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs.rs","lineNumber":850,"sourceCode":"            #[cfg(not(windows))]\n            std::fs::create_dir_all(dir)?;\n        }\n        #[cfg(unix)]\n        {\n            use std::os::unix::fs::PermissionsExt;\n            let perms = std::fs::Permissions::from_mode(0o700);\n            std::fs::set_permissions(&self.root, perms.clone())?;\n            // Secure the two top-level dot-dirs.\n            std::fs::set_permissions(self.root.join(\".local\"), perms.clone())?;\n            // `.config/` is retained only when a legacy migration left it in\n            // place. Fresh homes do not create it merely for env storage.\n            let config_dir = self.config_dir();\n            match std::fs::symlink_metadata(&config_dir) {\n                Ok(metadata) if metadata.is_dir() => {\n                    std::fs::set_permissions(config_dir, perms)?;\n                },\n                Ok(_) => {\n                    return Err(io::Error::new(\n                        io::ErrorKind::InvalidData,\n                        \"principal .config path is not a regular directory\",\n                    ));\n                },\n                Err(error) if error.kind() == io::ErrorKind::NotFound => {},\n                Err(error) => return Err(error),\n            }\n        }\n        Ok(())\n    }\n\n    // ── Path accessors ───────────────────────────────────────────────\n\n    /// Principal home root (`home/{principal}/`).\n    #[must_use]\n    pub fn root(&self) -> &Path {\n        &self.root\n    }","sourceCodeStart":832,"sourceCodeEnd":868,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs.rs#L832-L868","documentation":"Thrown by AstridDirs::ensure in crates/astrid-core/src/dirs.rs:850 when the principal .config directory path exists but symlink_metadata reports it is not a directory. ensure() wants to tighten permissions on an existing config dir; if the path is a regular file, symlink, or other non-directory, it aborts with InvalidData rather than clobbering it.","triggerScenarios":"Calling ensure() (directly or via install/sign-archive flows like sign_archive_with_runtime_key_in_home) when ~/.config or the configured config_dir is a plain file or symlink — e.g. a config file was created where a directory belongs, or a dotfile manager symlinked the path.","commonSituations":"Dotfile managers (GNU Stow, chezmoi) symlinking .config to a repo; a stray 'config' file created by a misconfigured tool; partial restore placing a tar of files at the config path.","solutions":["Replace the non-directory at the config path with a real directory (move the file aside, mkdir the path, restore contents).","Remove the symlink and let ensure() create a genuine directory, then re-link contents individually rather than the whole path.","Check `ls -la` / symlink_metadata on config_dir before install and fix its type.","Point Astrid's config_dir setting at a valid directory if the current path was misconfigured."],"exampleFix":"# before\n$ ls -la ~/.config\n.config -> dotfiles/config   # symlink\nerror: principal .config path is not a regular directory\n\n# after\n$ rm ~/.config\n$ mkdir ~/.config\n$ cp -r dotfiles/config/. ~/.config/","handlingStrategy":"validation","validationCode":"// Rust\nfn config_dir_ok(config_dir: &Path) -> bool {\n    match std::fs::symlink_metadata(config_dir) {\n        Ok(md) => md.is_dir(),\n        Err(e) => e.kind() == io::ErrorKind::NotFound, // absent is fine\n    }\n}","typeGuard":"fn is_real_directory(path: &Path) -> bool {\n    std::fs::symlink_metadata(path).map(|md| md.is_dir()).unwrap_or(false)\n}","tryCatchPattern":"match ensure_result {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData\n        && e.to_string().contains(\"not a regular directory\") => {\n        // move the file/symlink aside, mkdir the path, retry\n    }\n    r => r?,\n}","preventionTips":["Ensure ~/.config is a real directory, not a file or whole-path symlink","Configure dotfile managers to link individual entries, not .config itself","Check symlink_metadata on config paths before running install/sign flows","Restore config directories as directories, not loose files"],"tags":["filesystem","config","symlink","directory"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}