astrid-runtime/astrid · error

workspace state path must be a real directory, not a redirec

Error message

workspace state path must be a real directory, not a redirect or file: {}

What it means

This error is thrown when the workspace state directory path exists on disk but is either a symlink or a regular file instead of a real directory. The library requires state to live in a plain, physical directory so downstream canonicalization checks can guarantee it cannot be redirected.

Source

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

    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DescendantKind {
    Directory,
    File,
}

fn verify_state_dir_path(project_root: &Path, state_dir: &Path) -> io::Result<()> {
    crate::platform_fs::verify_no_redirects(project_root)?;
    crate::platform_fs::verify_no_redirects(state_dir)?;
    let metadata = match std::fs::symlink_metadata(state_dir) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()),
        Err(error) => return Err(error),
    };
    if metadata.file_type().is_symlink() || !metadata.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "workspace state path must be a real directory, not a redirect or file: {}",
                state_dir.display()
            ),
        ));
    }

    let canonical = std::fs::canonicalize(state_dir)?;
    if canonical != state_dir || canonical.parent() != Some(project_root) {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            format!(
                "workspace state directory escapes or redirects outside its selected path: {}",
                state_dir.display()
            ),
        ));
    }

View on GitHub (pinned to affd8760f4)

Solutions

  1. Remove the symlink or file at the state path and let the library create a real directory (e.g. `rm state_path && mkdir state_path`).
  2. If you intended redirection, move the data into the real path instead of symlinking.
  3. Re-run resolution after ensuring the path is a plain directory.

Example fix

# before
ln -s /mnt/data/.astrid-state .astrid-state
# after
mkdir .astrid-state && rsync -a /mnt/data/.astrid-state/ .astrid-state/
Defensive patterns

Strategy: validation

Validate before calling

use std::fs;
fn state_dir_ok(p: &std::path::Path) -> bool {
    match fs::symlink_metadata(p) {
        Ok(m) => !m.file_type().is_symlink() && m.is_dir(),
        Err(_) => true, // NotFound lets the library create it
    }
}

Type guard

fn is_real_dir(p: &std::path::Path) -> bool {
    std::fs::symlink_metadata(p).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)
}

Try / catch

match resolve(...) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
        std::fs::remove_file(state_dir).ok();
        std::fs::create_dir_all(state_dir)?;
        resolve(...)
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `resolve` (which invokes `verify_state_dir_path`) when `state_dir` exists but `symlink_metadata` reports `is_symlink()` true or `is_dir()` false.

Common situations: Users symlink their state directory to another disk or dotfiles repo; a file was accidentally created at the state path by a misbehaving tool; a previous cleanup left a placeholder file.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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