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
- Sanitize the component: lowercase it and strip/encode characters outside [0-9a-f] (or the allowed set) before passing it
- Reject or hash non-conforming identifiers — e.g. use a hex digest of the capsule name instead of the raw name
- Trim path separators and reject any input containing '/', '\\', '..', or '.' segments at the API boundary
- 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
- Hash raw identifiers into hex digests before using them as cache path components
- Reject user input containing '/', '\\', '..', or non-lowercase-hex chars at the boundary
- Lowercase and normalize ids when constructing cache keys
- Test the path-builder with adversarial inputs ('..', absolute paths, empty string)
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
- capsule materialization cache is redirected or not a directo
- cache contains a redirect or special entry: {}
- cache changed to a redirect or special entry: {}
- capsule {operation} '{}' is outside the explicit workspace p
- capsule materialization target is redirected or not a direct
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/afea1491c477e70b.
Report an issue: GitHub.