astrid-runtime/astrid · error

private path is redirected: {}

Error message

private path is redirected: {}

What it means

verify_no_redirects_unix refuses to operate on a path whose final component is a symbolic link, since a symlink could redirect security-sensitive private data outside the intended owner-only location. It fails with io::ErrorKind::InvalidData and the offending path in the message.

Source

Thrown at crates/astrid-core/src/platform_fs.rs:425

    #[cfg(unix)]
    {
        verify_no_redirects_unix(path)
    }

    #[cfg(not(any(unix, windows)))]
    {
        let _ = path;
        Ok(())
    }
}

#[cfg(unix)]
fn verify_no_redirects_unix(path: &Path) -> io::Result<()> {
    use nix::fcntl::{OFlag, openat};
    use nix::sys::stat::Mode;

    match std::fs::symlink_metadata(path) {
        Ok(metadata) if metadata.file_type().is_symlink() => Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("private path is redirected: {}", path.display()),
        )),
        Ok(metadata) if metadata.is_dir() => open_directory_no_follow_unix(path).map(drop),
        Ok(_) => {
            let parent = path.parent().ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("private path has no parent: {}", path.display()),
                )
            })?;
            let name = path.file_name().ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("private path has no file name: {}", path.display()),
                )
            })?;
            let directory = open_directory_no_follow_unix(parent)?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Remove the symlink and replace it with a real directory/file (e.g. rsync -L or cp -rL to materialize the target).
  2. Point your configuration at the real location instead of a symlinked path.
  3. Re-run ensure_private_directory to rebuild the private path as a real owner-only directory.

Example fix

// before (shell)
ln -s /mnt/data/.astrid ~/.astrid
// after (shell)
rm ~/.astrid && cp -rL /mnt/data/.astrid ~/.astrid && chmod 700 ~/.astrid
Defensive patterns

Strategy: validation

Validate before calling

let meta = std::fs::symlink_metadata(&path)?;
if meta.file_type().is_symlink() {
    return Err(format!("{} is a symlink; remove it before use", path.display()));
}

Type guard

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

Try / catch

if let Err(e) = verify_no_redirects(&path) {
    if e.kind() == io::ErrorKind::InvalidData {
        eprintln!("remove the symlink and materialize the real data: {e}");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Calling verify_no_redirects (Unix) with a path whose symlink_metadata reports a symlink file type — e.g. ~/.astrid or a child replaced by a symlink to elsewhere.

Common situations: Backup/restore tools that recreated state as symlinks; dotfile managers (stow, chezmoi) symlinking ~/.astrid into a repo; an attacker-planted symlink in a shared directory.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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