astrid-runtime/astrid · error
release executable is redirected or not regular: {name}
Error message
release executable is redirected or not regular: {name} What it means
For each executable, `validate_replacement_inputs` inspects `symlink_metadata` of the staged source. If the entry is a symlink or not a regular file (directory, FIFO, device, etc.), the call fails with `InvalidInput` — the library refuses to install executables that are redirects, which could silently point installations at attacker-controlled or unintended targets.
Source
Thrown at crates/astrid-core/src/platform_fs.rs:859
if !matches!(components.next(), Some(Component::Normal(_)))
|| components.next().is_some()
|| !unique.insert(*name)
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("invalid or duplicate executable name '{name}'"),
));
}
let source = extract_dir.join(name);
let metadata = std::fs::symlink_metadata(&source).map_err(|error| {
io::Error::new(
error.kind(),
format!("release archive is missing '{name}': {error}"),
)
})?;
if metadata.file_type().is_symlink() || !metadata.is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("release executable is redirected or not regular: {name}"),
));
}
}
Ok(())
}
#[cfg(not(windows))]
fn replace_executable_set_by_rename(
install_dir: &Path,
extract_dir: &Path,
names: &[&str],
) -> io::Result<()> {
let mut backups = Vec::new();
for name in names {
let live = install_dir.join(name);
if live.exists() {View on GitHub (pinned to affd8760f4)
Solutions
- Extract the archive with symlinks dereferenced/converted to real copies (e.g. `tar --dereference`) so each name is a regular file
- Rebuild the release archive so executables are regular files, not symlinks
- Point `extract_dir` at the real binary location, not a symlinked wrapper path; check each entry with `symlink_metadata(...).is_file()` before calling
Example fix
// before
Command::new("tar").args(["-xzf", archive, "-C", extract_dir]).status()?;
// after
Command::new("tar")
.args(["--dereference", "-xzf", archive, "-C", extract_dir])
.status()?; Defensive patterns
Strategy: validation
Validate before calling
for name in names {
let md = std::fs::symlink_metadata(extract_dir.join(name))?;
if md.file_type().is_symlink() || !md.is_file() {
return Err(anyhow!("{name} is not a regular file"));
}
} Type guard
fn is_regular_file_no_symlink(path: &Path) -> bool {
std::fs::symlink_metadata(path)
.map(|m| !m.file_type().is_symlink() && m.is_file())
.unwrap_or(false)
} Try / catch
match replace_executable_set(&install_dir, &extract_dir, names) {
Err(e) if e.to_string().starts_with("release executable is redirected") => {
eprintln!("archive contains symlinks; re-extract with --dereference: {e}");
}
other => other.map_err(Into::into),
} Prevention
- Extract archives with symlink dereferencing enabled
- Build release archives without internal symlinks
- Use symlink_metadata, not metadata, when vetting sources
- Treat non-regular files in archives as a packaging bug
When it happens
Trigger: The extracted archive contains a symlink (common on Unix for versioned binaries like `astrid -> astrid-1.2.3`), or `extract_dir.join(name)` resolves to a directory/pipe rather than a regular file; also when an extraction step preserved symlinks from a tarball.
Common situations: Unix archives (.tar.gz) built with symlinks inside; a malicious or mis-built release archive; extraction tools that create wrapper symlinks; a directory in extract_dir sharing the executable's name.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- workspace path must not contain redirects or unexpected file
- 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: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/198de921c4ffb094.
Report an issue: GitHub.