astrid-runtime/astrid · error

opaque asset path must be a directory: {}

Error message

opaque asset path must be a directory: {}

What it means

The signed channel pointer must name its release metadata asset exactly 'astrid-<version>-release.toml', derived from the pointer's own version. This ensure! fails when metadata_asset diverges from that canonical naming scheme, so the client would fetch a manifest it cannot bind to this pointer.

Source

Thrown at crates/astrid-build/src/archiver.rs:39

    let mut files = Vec::new();
    for name in OPAQUE_ASSET_DIRS {
        let root = base_dir.join(name);
        let metadata = match fs::symlink_metadata(&root) {
            Ok(metadata) => metadata,
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue,
            Err(error) => {
                return Err(error)
                    .with_context(|| format!("Failed to inspect asset path: {}", root.display()));
            },
        };
        if metadata.file_type().is_symlink() {
            anyhow::bail!(
                "opaque capsule asset directories cannot be symlinks: {}",
                root.display()
            );
        }
        if !metadata.is_dir() {
            anyhow::bail!("opaque asset path must be a directory: {}", root.display());
        }
        let mut pending = vec![root];
        while let Some(dir) = pending.pop() {
            for entry in fs::read_dir(&dir)
                .with_context(|| format!("Failed to read asset directory: {}", dir.display()))?
            {
                let entry = entry?;
                let path = entry.path();
                let file_type = entry.file_type()?;
                if file_type.is_symlink() {
                    anyhow::bail!(
                        "opaque capsule assets cannot be symlinks: {}",
                        path.display()
                    );
                }
                if file_type.is_dir() {
                    pending.push(path);
                } else if file_type.is_file() {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Set release.metadata_asset to exactly "astrid-<version>-release.toml" using the canonical version from the pointer.
  2. Regenerate the signed pointer with the release tooling so all derived fields are consistent.
  3. Re-sign and re-upload the pointer, then re-run parse_channel.

Example fix

# before
version = "1.2.3"
metadata_asset = "astrid-release.toml"
# after
version = "1.2.3"
metadata_asset = "astrid-1.2.3-release.toml"
Defensive patterns

Strategy: validation

Validate before calling

fn metadata_asset_ok(version: &str, asset: &str) -> bool {
    asset == format!("astrid-{version}-release.toml")
}

Try / catch

if let Err(e) = parse_channel(&bytes, channel, now) {
    if e.to_string().contains("metadata asset is invalid") { /* fix asset name and re-sign */ }
    return Err(e.into());
}

Prevention

When it happens

Trigger: validate_pointer (via parse_channel or enforce_continuity) sees a ChannelPointer whose release.metadata_asset is not the string format!("astrid-{version}-release.toml") for the canonical version.

Common situations: Renamed or vendored manifest asset in a hand-maintained channel file; a tool from an older naming convention; typo in the asset field; pointer regenerated for a different version but asset name left stale.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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