astrid-runtime/astrid · error

opaque capsule assets cannot be symlinks

Error message

opaque capsule assets cannot be symlinks: {}

What it means

In the FUSE storage provider's hidden service mode, run_launch computes the parent-challenge for the readiness handshake via storage_provider_service_ready_challenge and maps any error it returns into an anyhow error using the error's Display text ('{error}'). It indicates the launch handshake inputs (parent token, mount id, paths) failed challenge derivation, so the service cannot prove itself to the parent process.

Solutions

  1. Inspect the inner error text to see which challenge input failed, then fix the parent-side launch payload accordingly.
  2. Ensure launch.parent.token is a valid, non-empty token produced by storage_provider_service_ready_challenge's counterpart on the parent side.
  3. Verify control_path, resource_path and callback_path are absolute, valid paths and mount_id is a well-formed UUID.

Example fix

// before
let challenge = storage_provider_service_ready_challenge(
    &launch.parent.token, /* token from hand-built launch JSON */ ...
).map_err(|error| anyhow::anyhow!(error))?;
// after
// construct the launch via the parent SDK so the token is derived from the same inputs
let launch = StorageProviderServiceLaunchV1::build(&lease, &parent)?;
let challenge = storage_provider_service_ready_challenge(
    &launch.parent.token, ...
).map_err(|error| anyhow::anyhow!(error))?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn launch_inputs_ok(launch: &StorageProviderServiceLaunchV1) -> bool {
    !launch.parent.token.is_empty()
        && launch.lease.mount_id.as_uuid() != uuid::Uuid::nil()
        && !launch.control_path.as_os_str().is_empty()
}

Try / catch

match run_launch(launch).await {
    Err(e) => { eprintln!("FUSE launch failed: {e:#}"); std::process::exit(1); },
    Ok(()) => {},
}

Prevention

When it happens

Trigger: run (FUSE service mode) receives a StorageProviderServiceLaunchV1 on stdin, passes validate_launch, then storage_provider_service_ready_challenge returns Err — e.g. malformed or empty parent token, or invalid path/UUID inputs — and run_launch propagates it verbatim.

Common situations: Parent process passed an empty or wrongly-encoded parent.token; control/resource/callback paths contain invalid characters or are empty; mount_id UUID serialization mismatch between parent and provider versions.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

        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() {
                    files.push(path);
                }
            }
        }
    }
    files.sort();
    Ok(files)
}

/// Packages a set of files and directories into a single `.capsule` (tar.gz) archive.
pub(crate) fn pack_capsule_archive(

View on GitHub (pinned to affd8760f4)