astrid-runtime/astrid · error
workspace path must not contain redirects or unexpected file
Error message
workspace path must not contain redirects or unexpected file types: {} What it means
While walking each component of the descendant path, resolve_descendant stats each intermediate/current path and rejects it with InvalidInput if it is a symlink, if the final component is not a regular file when a file was requested, or if any non-final component is not a directory. This enforces that the resolved route contains no redirects and matches the requested kind.
Source
Thrown at crates/astrid-core/src/workspace_security.rs:199
let mut current = self.state_dir.clone();
for (index, component) in components.iter().enumerate() {
let Component::Normal(component) = component else {
unreachable!("components validated above")
};
current.push(component);
let metadata = match std::fs::symlink_metadata(¤t) {
Ok(metadata) => metadata,
Err(error) if error.kind() == io::ErrorKind::NotFound => continue,
Err(error) => return Err(error),
};
let final_component = index == components.len().saturating_sub(1);
let expected_file = final_component && kind == DescendantKind::File;
if metadata.file_type().is_symlink()
|| (expected_file && !metadata.is_file())
|| (!expected_file && !metadata.is_dir())
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"workspace path must not contain redirects or unexpected file types: {}",
current.display()
),
));
}
if std::fs::canonicalize(¤t)? != current {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"workspace path redirects from its selected target: {}",
current.display()
),
));
}
}
Ok(self.state_dir.join(relative))View on GitHub (pinned to affd8760f4)
Solutions
- Ensure the final component's type matches the call: use resolve_file for files and resolve_directory for directories.
- Replace symlinks inside the workspace with real files/directories.
- Check what exists at the reported path (ls -la) and reconcile it with what the code expects.
- If symlinked config is intentional, copy the file into the workspace instead of linking it.
Example fix
// before
let f = ws.resolve_file(Path::new("config/settings.toml"))?; // settings.toml is actually a directory
// after
let meta = std::fs::metadata(root.join("config/settings.toml"))?;
let entry = if meta.is_file() {
ws.resolve_file(Path::new("config/settings.toml"))?
} else {
ws.resolve_directory(Path::new("config/settings.toml"))?
}; Defensive patterns
Strategy: validation
Validate before calling
fn entry_matches(p: &Path, want_file: bool) -> bool {
match std::fs::metadata(p) {
Ok(m) => !m.file_type().is_symlink() && if want_file { m.is_file() } else { m.is_dir() },
Err(_) => false,
}
} Prevention
- Match the API to intent: resolve_file only for real files, resolve_directory only for dirs
- Avoid symlink-farm layouts (e.g. symlinked dotfiles) inside workspaces
- Re-check path types when another process may mutate the tree concurrently
When it happens
Trigger: resolve_file called on a path whose final component is a directory (or vice versa resolve_directory on a file); any component of the path being a symlink; intermediate components that are regular files (e.g. treating "a.txt/b" style paths).
Common situations: Caller assuming a file exists but a directory with the same name is present (or the file was replaced by a symlink); symlinked dotfile setups (e.g. dotfiles managed with symlinks into the workspace); race where another process swaps a directory for a symlink mid-walk.
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
- leftover capsule authority receipt is not a regular file: {}
- legacy capsule authority root is not a regular directory: {}
- legacy capsule authority root contains a non-regular entry:
- legacy capsule authority root 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/53f01f0c08817a97.
Report an issue: GitHub.