astrid-runtime/astrid · error

cache changed to a redirect or special entry: {}

Error message

cache changed to a redirect or special entry: {}

What it means

remove_cache_tree in astrid-capsule-install refuses to delete a materialization-cache path whose symlink_metadata shows it is a symlink or neither a directory nor a regular file (e.g. fifo, device, socket). The library treats symlinks and special files as 'redirects' that could point outside the cache, so instead of following/removing them blindly it bails to protect against path-redirect attacks or corrupted cache state. The word 'changed' indicates a TOCTOU check: the entry was expected to be a plain dir/file but mutated between validation and removal.

Source

Thrown at crates/astrid-capsule-install/src/paths.rs:227

                "cache contains a redirect or special entry: {}",
                path.display()
            );
        }
        if metadata.is_dir() {
            validate_cache_tree(&path)?;
        } else {
            astrid_core::platform_fs::verify_no_redirects(&path)
                .with_context(|| format!("verify cache file {}", path.display()))?;
        }
    }
    Ok(())
}

fn remove_cache_tree(path: &Path) -> anyhow::Result<()> {
    let metadata = std::fs::symlink_metadata(path)
        .with_context(|| format!("inspect cache path {}", path.display()))?;
    if metadata.file_type().is_symlink() || (!metadata.is_dir() && !metadata.is_file()) {
        anyhow::bail!(
            "cache changed to a redirect or special entry: {}",
            path.display()
        );
    }
    if metadata.is_dir() {
        for entry in std::fs::read_dir(path).with_context(|| format!("read {}", path.display()))? {
            remove_cache_tree(
                &entry
                    .with_context(|| format!("read cache entry under {}", path.display()))?
                    .path(),
            )?;
        }
        std::fs::remove_dir(path).with_context(|| format!("remove {}", path.display()))?;
    } else {
        std::fs::remove_file(path).with_context(|| format!("remove {}", path.display()))?;
    }
    Ok(())
}

View on GitHub (pinned to affd8760f4)

Solutions

  1. Inspect the reported path with `ls -la` and replace the symlink/special file with a real directory or file (or remove it yourself) before re-running.
  2. Clear the offending entry manually (`rm` the symlink, `mkfifo`/socket leftovers) and let the tool rebuild the cache.
  3. Stop processes that write into the cache concurrently so entries cannot be swapped mid-removal.
  4. If symlinking the cache is intentional, it is unsupported: relocate the whole Astrid home instead of individual cache entries.

Example fix

// before: symlinked cache entry pointing to another disk
ln -s /mnt/bigdisk/astrid-cache/entry ~/.astrid/cache/entry
clear_capsule_materialization_cache(...) // -> cache changed to a redirect or special entry
// after: remove the symlink and let the tool own the path
rm ~/.astrid/cache/entry
mkdir ~/.astrid/cache/entry
Defensive patterns

Strategy: validation

Validate before calling

let md = std::fs::symlink_metadata(path)?;
if md.file_type().is_symlink() || (!md.is_dir() && !md.is_file()) {
    // resolve before invoking the cache-clearing API
    eprintln!("cache entry {} is a redirect/special file; fix manually", path.display());
}

Type guard

fn is_plain_entry(md: &std::fs::Metadata) -> bool {
    !md.file_type().is_symlink() && (md.is_dir() || md.is_file())
}

Try / catch

match clear_capsule_materialization_cache() {
    Err(e) if e.to_string().contains("redirect or special entry") => {
        // repair path manually or wipe the cache root, then retry once
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling clear_capsule_materialization_cache (which recurses remove_cache_tree) while a cache entry is a symlink to elsewhere, or a special file (fifo/socket/device). Also fires if another process or attacker swaps a real cache entry for a symlink between the validate_cache_tree pass and the removal pass.

Common situations: Manual tinkering with the Astrid home cache directory (e.g. ln -s to move a large cache to another disk); a build tool creating fifos/sockets inside the cache; concurrent runs of the tool racing on the same cache tree; restored-from-backup caches containing symlinks.

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/c108951ff1414c7e. Report an issue: GitHub.