astrid-runtime/astrid · error

Distro.toml has no parent directory

Error message

Distro.toml has no parent directory

What it means

Thrown by `resolve_local_capsule_archive` when the local Distro.toml manifest path has no parent directory component, so relative member paths inside the manifest cannot be resolved against a root. In practice this occurs only for degenerate paths like a bare "Distro.toml" reference with no directory portion. Security-relevant relatives: traversal and symlink escapes from this root are checked afterwards and fail closed.

Source

Thrown at crates/astrid-cli/src/commands/distro/local_source.rs:57

/// The returned path is canonicalized so the subsequent copy and hash cover
/// the same filesystem object that passed containment checks.
pub(crate) fn resolve_local_capsule_archive(
    source: &str,
    manifest_path: Option<&Path>,
) -> anyhow::Result<Option<PathBuf>> {
    if !is_local_capsule_source(source) {
        return Ok(None);
    }
    let Some(manifest_path) = manifest_path else {
        bail!(
            "local capsule source {source:?} requires a local authenticated Distro.toml; \
             remote manifests cannot resolve relative members"
        );
    };

    let root = manifest_path
        .parent()
        .ok_or_else(|| anyhow::anyhow!("Distro.toml has no parent directory"))?;
    let source_path = Path::new(source);
    let candidate = if source_path.is_absolute() {
        source_path.to_path_buf()
    } else {
        root.join(source_path)
    };
    if source_path
        .components()
        .any(|component| matches!(component, Component::ParentDir))
    {
        bail!("local capsule source {source:?} escapes the authenticated Distro.toml directory");
    }

    let canonical_root = root
        .canonicalize()
        .with_context(|| format!("failed to resolve Distro.toml directory {}", root.display()))?;
    let canonical_path = candidate
        .canonicalize()

View on GitHub (pinned to affd8760f4)

Solutions

  1. Pass the manifest with an explicit directory prefix: `./Distro.toml` instead of `Distro.toml`.
  2. Use an absolute path to the manifest: `--manifest /path/to/distro/Distro.toml`.
  3. cd into the manifest's directory and reference it as `./Distro.toml`.
  4. If constructing the path in code, canonicalize it first (`path.canonicalize()`) so a parent component exists.

Example fix

// before (shell)
astrid distro install --manifest Distro.toml
// after (shell)
astrid distro install --manifest "$PWD/Distro.toml"
Defensive patterns

Strategy: validation

Validate before calling

let manifest = std::path::PathBuf::from(manifest_arg);
let manifest = if manifest.is_relative() {
    std::env::current_dir()?.join(manifest)
} else { manifest };
if manifest.parent().is_none() {
    return Err(anyhow!("manifest path must include a directory component"));
}

Try / catch

match resolve_local_capsule_archive(&manifest, source) {
    Err(e) if e.to_string().contains("no parent directory") => {
        let abs = std::fs::canonicalize("Distro.toml")?;
        resolve_local_capsule_archive(&abs, source)
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling distro install/resolve with a manifest path such as `Distro.toml` (no directory prefix) so `Path::parent()` returns None, and the manifest references relative member archives.

Common situations: Scripting `--manifest Distro.toml` from a working directory where the path was reduced to a bare filename; passing a programmatically constructed PathBuf of just a file name.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/9f7866e1ecd08676. Report an issue: GitHub.