astrid-runtime/astrid · error
capsule materialization cache is redirected or not a directo
Error message
capsule materialization cache is redirected or not a directory: {} What it means
clear_capsule_materialization_cache refuses to wipe the materialization cache root if symlink_metadata shows it is a symlink or not a directory. This prevents a redirected cache root (an attacker- or misconfiguration-planted symlink) from causing deletion of an arbitrary directory. NotFound is tolerated (nothing to clean); any other inspection failure is wrapped in a context error.
Source
Thrown at crates/astrid-capsule-install/src/paths.rs:183
Ok(())
}
/// Remove every disposable user capsule materialization from the runtime
/// cache after validating the complete tree without following redirects.
///
/// Durable packages are never touched. A fresh materialization is created
/// from a verified storage snapshot when needed, so deleting stale or
/// interrupted cache generations at boot is safe and avoids reusing an
/// unverified crash residue.
pub fn clear_capsule_materialization_cache(home: &AstridHome) -> anyhow::Result<()> {
let root = home.run_dir().join("capsules");
let metadata = match std::fs::symlink_metadata(&root) {
Ok(metadata) => metadata,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(error) => return Err(error).context("inspect capsule materialization cache"),
};
if metadata.file_type().is_symlink() || !metadata.is_dir() {
anyhow::bail!(
"capsule materialization cache is redirected or not a directory: {}",
root.display()
);
}
validate_cache_tree(&root)?;
for entry in std::fs::read_dir(&root).context("read capsule materialization cache")? {
let path = entry
.context("read capsule materialization cache entry")?
.path();
remove_cache_tree(&path)?;
}
Ok(())
}
fn validate_cache_tree(path: &Path) -> anyhow::Result<()> {
astrid_core::platform_fs::verify_no_redirects(path)
.with_context(|| format!("verify cache path {}", path.display()))?;
for entry in std::fs::read_dir(path).with_context(|| format!("read {}", path.display()))? {View on GitHub (pinned to affd8760f4)
Solutions
- Remove the symlink or file at the cache root path, then re-run the cleanup so the library can recreate a real directory
- Fix whatever creates the cache root so it is always a real directory owned by the runtime
- Point the cache root configuration at the intended directory rather than a link
- If redirecting the cache is a legitimate need, relocate the configured root itself instead of symlinking
Example fix
// before $ ln -s /mnt/bigdisk/capsule-cache ~/.cache/astrid/capsules // after $ rm ~/.cache/astrid/capsules $ mkdir -p ~/.cache/astrid/capsules # or reconfigure the cache root to /mnt/bigdisk/capsule-cache
Defensive patterns
Strategy: validation
Validate before calling
fn cache_root_safe(root: &Path) -> anyhow::Result<bool> {
match std::fs::symlink_metadata(root) {
Ok(m) => Ok(!m.file_type().is_symlink() && m.is_dir()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(true),
Err(e) => Err(e.into()),
}
} Type guard
fn is_real_directory(root: &Path) -> bool {
std::fs::symlink_metadata(root).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)
} Try / catch
if let Err(e) = clear_capsule_materialization_cache(&root) {
if e.to_string().contains("redirected or not a directory") {
// remove the symlink/file at root, recreate a real dir, retry once
} else { return Err(e); }
} Prevention
- Never symlink the configured cache root; change the configuration instead
- Have the runtime create the cache root itself with create_dir_all
- Alert on any file or link appearing at the cache root path
- In tests/CI, assert the cache root is a real directory before cleanup
When it happens
Trigger: Calling clear_capsule_materialization_cache when the cache root path is a symlink (e.g. a user linked it to another location), or a regular file was created at the cache root path, or a previous misconfiguration pointed the cache at a non-directory path.
Common situations: Users symlinking the cache to a bigger disk or shared location; leftover file occupying the cache path after a failed install; container images where the cache path is a mounted file; tests (like cache_cleanup_removes_crash_residue) that pre-create the path incorrectly.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- cache contains a redirect or special entry: {}
- cache changed to a redirect or special entry: {}
- capsule cache path is redirected: {error}
- leftover capsule authority receipt is not a regular file: {}
- legacy capsule authority root is not a regular directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7ff2454c30c621a4.
Report an issue: GitHub.