astrid-runtime/astrid · error

workspace root is not a directory: {}

Error message

workspace root is not a directory: {}

What it means

After canonicalizing the project root and verifying it does not redirect through symlinks, WorkspaceSelection::resolve stats the path and rejects it with InvalidInput if it is not a directory. A workspace root must be an actual directory; pointing it at a regular file (or special file) is a configuration error.

Source

Thrown at crates/astrid-core/src/workspace_security.rs:36

    project_root: PathBuf,
    state_dir: PathBuf,
    layout: WorkspaceLayout,
}

impl WorkspaceSelection {
    pub(super) fn resolve(project_root: &Path, layout: WorkspaceLayout) -> io::Result<Self> {
        let project_root = std::fs::canonicalize(project_root).map_err(|error| {
            io::Error::new(
                error.kind(),
                format!(
                    "failed to resolve workspace root {}: {error}",
                    project_root.display()
                ),
            )
        })?;
        crate::platform_fs::verify_no_redirects(&project_root)?;
        if !std::fs::metadata(&project_root)?.is_dir() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!(
                    "workspace root is not a directory: {}",
                    project_root.display()
                ),
            ));
        }

        let state_dir = project_root.join(layout.state_dir_name());
        verify_state_dir_path(&project_root, &state_dir)?;
        Ok(Self {
            project_root,
            state_dir,
            layout,
        })
    }

    /// Canonical project root used by every workspace filesystem consumer.

View on GitHub (pinned to affd8760f4)

Solutions

  1. Point project_root at the workspace directory, not a file inside it.
  2. If the directory was replaced by a file, restore it (recreate the directory and move contents back).
  3. Add an is_dir() precheck on the configured path before constructing the workspace selection.
  4. Fix mounts/symlinks that shadow the intended directory.

Example fix

// before
let sel = WorkspaceSelection::resolve(Path::new("/srv/app/Cargo.toml"), layout)?;
// after
let sel = WorkspaceSelection::resolve(Path::new("/srv/app"), layout)?;
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::metadata(&root)?;
assert!(meta.is_dir(), "project root must be a directory: {}", root.display());

Prevention

When it happens

Trigger: Calling WorkspaceSelection::resolve with a project_root that canonicalizes to a regular file, FIFO, socket, or device node instead of a directory.

Common situations: Config pointing at a file like project.toml instead of the project directory; a mount or symlink swapping the directory for a file; automation passing the wrong variable (file path vs directory path).

Related errors


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