astrid-runtime/astrid · error
workspace capsule portal contains a redirect: {}
Error message
workspace capsule portal contains a redirect: {} What it means
While walking the workspace capsule portal, an entry inside the tree is itself a symlink. The barrier treats any redirect inside the portal as a potential path-traversal vector and aborts the inventory with InvalidData rather than following it. The message names the offending child path.
Source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/host_fs.rs:165
"workspace capsule portal is not a regular directory: {}",
root.display()
),
));
}
astrid_core::platform_fs::verify_no_redirects(root)?;
let mut targets = Vec::new();
let mut stack = vec![root.to_path_buf()];
while let Some(dir) = stack.pop() {
let mut entries = fs::read_dir(&dir)
.map_err(io::Error::other)?
.collect::<Result<Vec<_>, _>>()
.map_err(io::Error::other)?;
entries.sort_by_key(std::fs::DirEntry::file_name);
for entry in entries {
let path = entry.path();
let metadata = fs::symlink_metadata(&path).map_err(io::Error::other)?;
if metadata.file_type().is_symlink() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"workspace capsule portal contains a redirect: {}",
path.display()
),
));
}
if !metadata.is_dir() {
continue;
}
astrid_core::platform_fs::verify_no_redirects(&path)?;
if path.join("Capsule.toml").is_file() {
targets.push(path.clone());
if targets.len() > MAX_WORKSPACE_TARGETS {
return Err(io::Error::other(
"workspace capsule portal exceeds target limit",
));
}View on GitHub (pinned to affd8760f4)
Solutions
- Replace the reported symlink with a real copy of its target: rm the link, then `cp -a` or `mv` the target into place.
- Exclude symlinks from the portal directory, keeping only physical capsule directories containing Capsule.toml.
- If the link is required, relocate the linked content physically under the portal and update consumers to the new path.
- Re-run the migration after cleaning; the error names the exact path each time, so fix iteratively.
Example fix
// before ln -s ~/shared-capsule ~/.astrid/workspaces/team-capsule // after rm ~/.astrid/workspaces/team-capsule cp -a ~/shared-capsule ~/.astrid/workspaces/team-capsule
Defensive patterns
Strategy: validation
Validate before calling
fn tree_has_symlinks(root: &std::path::Path) -> std::io::Result<Vec<std::path::PathBuf>> {
let mut hits = Vec::new();
for entry in walkdir_like(root) {
if std::fs::symlink_metadata(&entry)?.file_type().is_symlink() {
hits.push(entry);
}
}
Ok(hits) // must be empty before migration
} Type guard
fn is_plain_entry(m: &std::fs::Metadata) -> bool {
!m.file_type().is_symlink()
} Try / catch
match collect_workspace_targets(root) {
Ok(t) => t,
Err(e) if e.to_string().contains("contains a redirect") => {
let p = extract_path(&e.to_string());
eprintln!("replace symlink with a real copy: {p}");
Vec::new()
}
Err(e) => return Err(e.into()),
} Prevention
- Keep convenience symlinks (dotfiles, shared assets) outside the portal tree
- Run `find <portal> -type l` as a pre-migration checklist step
- Teach package managers/editors to place links elsewhere (e.g. global cache dirs)
When it happens
Trigger: collect_workspace_targets iterates sorted directory entries and calls symlink_metadata on each child; any child whose file type is a symlink raises this immediately, before recursion. Also triggered if the portal root's own verify_no_redirects pass misses a deeper entry and the walk reaches it.
Common situations: Editors or package managers creating convenience symlinks (e.g. node_modules links, shared asset links) inside a workspace; users linking a capsule dir into their dotfiles; migration of data between machines that preserved 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
- inspect projected file {}: {error}
- legacy state source is redirected: {}
- legacy state source contains a redirect: {}
- workspace capsule portal is not a regular directory: {}
- directory symlink {} not allowed in capsule source tree (ref
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/503ef1888b8ff65e.
Report an issue: GitHub.