astrid-runtime/astrid · error
capsule materialization target is redirected: {error}
Error message
capsule materialization target is redirected: {error} What it means
This error is thrown by `repair_published_materialization` when, before wiping a stale capsule materialization directory, `astrid_core::platform_fs::verify_no_redirects(target)` finds symlinks or other redirect entries inside the target tree. The kernel refuses to `remove_dir_all` a directory whose contents are redirected (e.g. contain symlinks), because destroying it could follow links and damage files outside the cache directory. It is a defensive integrity check on the durable cache path before deletion.
Source
Thrown at crates/astrid-kernel/src/capsule_materialization.rs:197
let target_metadata = match std::fs::symlink_metadata(target) {
Ok(metadata) => Some(metadata),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
Err(error) => return Err(anyhow::anyhow!("inspect capsule materialization: {error}")),
};
if let Some(metadata) = target_metadata {
if metadata.file_type().is_symlink() || !metadata.is_dir() {
anyhow::bail!("capsule materialization target is redirected or not a directory");
}
if let Ok(bound_manifest) =
astrid_capsule::discovery::load_manifest(&target.join("Capsule.toml"))
&& self
.verify_published_materialization(target, principal, &bound_manifest, snapshot)
.is_ok()
{
return Ok(bound_manifest);
}
astrid_core::platform_fs::verify_no_redirects(target).map_err(|error| {
anyhow::anyhow!("capsule materialization target is redirected: {error}")
})?;
std::fs::remove_dir_all(target).map_err(|error| {
anyhow::anyhow!("remove stale capsule materialization: {error}")
})?;
}
astrid_capsule_install::materialize_capsule_package(snapshot.package(), target)
.map_err(|error| anyhow::anyhow!("materialize durable capsule package: {error:#}"))?;
let bound_manifest = astrid_capsule::discovery::load_manifest(&target.join("Capsule.toml"))
.map_err(|error| anyhow::anyhow!(error))?;
self.verify_published_materialization(target, principal, &bound_manifest, snapshot)?;
Ok(bound_manifest)
}
/// Recheck the immutable publication after taking activation locks.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) fn confirm_published_materialization(
&self,
dir: &Path,View on GitHub (pinned to affd8760f4)
Solutions
- Remove the target directory manually (after inspecting it for symlinks pointing outside the cache) so the next run starts from a clean, redirect-free directory.
- Find the symlink entries reported in the underlying `verify_no_redirects` error and replace them with real files/directories, then re-run the operation.
- Stop any tooling (rsync link-dest, pnpm-style symlinking, overlay mounts) that is materializing the cache directory with symlinks instead of real content.
- If the target is intentionally linked, point the materialization target at a dedicated directory path that nothing else manages.
Example fix
// before (developer shared cache entries via symlinks) $ ln -s /shared/build/wasm pkg/out.wasm // after (use real files, or a fresh target) $ rm -rf ~/.cache/astrid/capsules/<name>-<hash> $ astrid capsule install <name> // re-materializes real files
Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn target_is_clean_real_dir(target: &Path) -> Result<(), String> {
let md = std::fs::symlink_metadata(target)
.map_err(|e| format!("cannot stat target {}: {e}", target.display()))?;
if md.file_type().is_symlink() || !md.is_dir() {
return Err("target is a symlink or not a directory".into());
}
// Reject any symlinked entry inside the tree before invoking the library.
fn scan(dir: &Path) -> Result<(), String> {
for entry in std::fs::read_dir(dir).map_err(|e| e.to_string())? {
let entry = entry.map_err(|e| e.to_string())?;
if entry.file_type().map_err(|e| e.to_string())?.is_symlink() {
return Err(format!("symlink inside target: {}", entry.path().display()));
}
if entry.file_type().map_err(|e| e.to_string())?.is_dir() {
scan(&entry.path())?;
}
}
Ok(())
}
scan(target)
} Type guard
fn is_real_directory(target: &Path) -> bool {
std::fs::symlink_metadata(target)
.map(|md| md.is_dir() && !md.file_type().is_symlink())
.unwrap_or(false)
} Prevention
- Never place symlinks inside capsule cache/materialization directories; copy real files instead.
- Disable symlink-preserving sync tools (rsync -a with link-dest, pnpm-style linking) for the cache path.
- Treat the cache directory as library-managed: never hand-edit or link entries inside it.
- Keep the materialization target on a plain local filesystem, not an overlay or network mount that introduces links.
When it happens
Trigger: Calling `ensure_published_materialization` or `capture_bound_materialization` when the existing target directory fails `verify_published_materialization` (stale manifest/content) AND `verify_no_redirects` then detects a symlink or redirect entry inside the target directory tree.
Common situations: A developer or tool manually symlinked files inside the capsule cache directory to elsewhere (e.g. to shared build artifacts); a cache directory was partially replaced with links by a custom sync tool; a cache-shared volume or overlayfs setup introduces symlinked entries; a previous buggy version of the materializer wrote symlinks.
Related errors
- cache changed to a redirect or special entry: {}
- leftover capsule authority receipt is not a regular file: {}
- quarantined capsule authority bytes changed: {}
- leftover capsule authority receipt changed before retirement
- 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/e5d5951413c05e6b.
Report an issue: GitHub.