astrid-runtime/astrid · error · io::Error

principal .config path is not a regular directory

Error message

principal .config path is not a regular directory

What it means

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.

Source

Thrown at crates/astrid-core/src/dirs.rs:850

            #[cfg(not(windows))]
            std::fs::create_dir_all(dir)?;
        }
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let perms = std::fs::Permissions::from_mode(0o700);
            std::fs::set_permissions(&self.root, perms.clone())?;
            // Secure the two top-level dot-dirs.
            std::fs::set_permissions(self.root.join(".local"), perms.clone())?;
            // `.config/` is retained only when a legacy migration left it in
            // place. Fresh homes do not create it merely for env storage.
            let config_dir = self.config_dir();
            match std::fs::symlink_metadata(&config_dir) {
                Ok(metadata) if metadata.is_dir() => {
                    std::fs::set_permissions(config_dir, perms)?;
                },
                Ok(_) => {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        "principal .config path is not a regular directory",
                    ));
                },
                Err(error) if error.kind() == io::ErrorKind::NotFound => {},
                Err(error) => return Err(error),
            }
        }
        Ok(())
    }

    // ── Path accessors ───────────────────────────────────────────────

    /// Principal home root (`home/{principal}/`).
    #[must_use]
    pub fn root(&self) -> &Path {
        &self.root
    }

View on GitHub (pinned to affd8760f4)

Solutions

  1. Replace the non-directory at the config path with a real directory (move the file aside, mkdir the path, restore contents).
  2. Remove the symlink and let ensure() create a genuine directory, then re-link contents individually rather than the whole path.
  3. Check `ls -la` / symlink_metadata on config_dir before install and fix its type.
  4. Point Astrid's config_dir setting at a valid directory if the current path was misconfigured.

Example fix

# before
$ ls -la ~/.config
.config -> dotfiles/config   # symlink
error: principal .config path is not a regular directory

# after
$ rm ~/.config
$ mkdir ~/.config
$ cp -r dotfiles/config/. ~/.config/
Defensive patterns

Strategy: validation

Validate before calling

// Rust
fn config_dir_ok(config_dir: &Path) -> bool {
    match std::fs::symlink_metadata(config_dir) {
        Ok(md) => md.is_dir(),
        Err(e) => e.kind() == io::ErrorKind::NotFound, // absent is fine
    }
}

Type guard

fn is_real_directory(path: &Path) -> bool {
    std::fs::symlink_metadata(path).map(|md| md.is_dir()).unwrap_or(false)
}

Try / catch

match ensure_result {
    Err(e) if e.kind() == io::ErrorKind::InvalidData
        && e.to_string().contains("not a regular directory") => {
        // move the file/symlink aside, mkdir the path, retry
    }
    r => r?,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/a05a1efb069dbfbc. Report an issue: GitHub.