astrid-runtime/astrid · error

{label} is not a canonical cache component

Error message

{label} is not a canonical cache component

What it means

validate_cache_component asserts that a path segment used to build a cache location (e.g. a capsule name or version component) is canonical: only lowercase hex/digit-safe characters as checked by the preceding validation, and every std::path::Component is Normal — no '.', '..', prefixes, or separators. Any component that could escape or alias the cache tree is rejected with this error before any path is constructed.

Source

Thrown at crates/astrid-capsule-install/src/paths.rs:163

fn validate_cache_component(label: &str, value: &str, digest: bool) -> anyhow::Result<()> {
    let valid = !value.is_empty()
        && value.len() <= 128
        && value
            .bytes()
            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-'))
        && !value.starts_with('.')
        && !value.ends_with('.')
        && !value.contains("..")
        && (!digest
            || (value.len() == 64
                && value
                    .bytes()
                    .all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))))
        && Path::new(value)
            .components()
            .all(|component| matches!(component, std::path::Component::Normal(_)));
    if !valid {
        anyhow::bail!("{label} is not a canonical cache component")
    }
    Ok(())
}

/// Remove every disposable user capsule materialization from the runtime
/// cache after validating the complete tree without following redirects.
///
/// Durable packages are never touched. A fresh materialization is created
/// from a verified storage snapshot when needed, so deleting stale or
/// interrupted cache generations at boot is safe and avoids reusing an
/// unverified crash residue.
pub fn clear_capsule_materialization_cache(home: &AstridHome) -> anyhow::Result<()> {
    let root = home.run_dir().join("capsules");
    let metadata = match std::fs::symlink_metadata(&root) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
        Err(error) => return Err(error).context("inspect capsule materialization cache"),
    };

View on GitHub (pinned to affd8760f4)

Solutions

  1. Sanitize the component: lowercase it and strip/encode characters outside [0-9a-f] (or the allowed set) before passing it
  2. Reject or hash non-conforming identifiers — e.g. use a hex digest of the capsule name instead of the raw name
  3. Trim path separators and reject any input containing '/', '\\', '..', or '.' segments at the API boundary
  4. Log the offending label and value to identify which caller passed a non-canonical component

Example fix

// before
let dir = resolve_cache_target_dir(&cache_root, capsule_name)?; // may contain ':' or '/'
// after
let component: String = capsule_name.bytes().filter(|b| b.is_ascii_digit() || matches!(b, b'a'..=b'f')).map(|b| b as char).collect();
let dir = resolve_cache_target_dir(&cache_root, &component)?;
Defensive patterns

Strategy: validation

Validate before calling

fn canonical_cache_component(value: &str) -> bool {
    !value.is_empty()
        && value.bytes().all(|b| b.is_ascii_digit() || matches!(b, b'a'..=b'f'))
        && std::path::Path::new(value).components().all(|c| matches!(c, std::path::Component::Normal(_)))
}

Type guard

fn as_cache_component(s: &str) -> Option<&str> {
    canonical_cache_component(s).then_some(s)
}

Try / catch

match resolve_cache_target_dir(&root, comp) {
    Err(e) if e.to_string().contains("not a canonical cache component") => {
        // hash or sanitize the identifier and retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling resolve_cache_target_dir with a capsule name/version/label containing path separators, '..', leading dots, or non-lowercase-hex characters where a hex-digest form is required, so the `valid` conjunction evaluates false.

Common situations: Capsule ids with uppercase or ':', '/', '\\' characters; passing an unnormalized version like '1.0.0-beta+build' or a raw user string; on Windows, drive-letter components; an empty string component.

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/afea1491c477e70b. Report an issue: GitHub.