astrid-runtime/astrid · error
local capsule source
Error message
local capsule source {source:?} escapes the authenticated Distro.toml directory What it means
As a cheap pre-canonicalization check, resolve_local_capsule_archive rejects any source path containing a `..` (ParentDir) component. Such a path could climb out of the authenticated Distro.toml directory, so it is treated as an escape attempt and bails before any filesystem access.
Solutions
- Rewrite the source as a path relative to the Distro.toml directory without `..` segments
- Move or symlink the capsule into the manifest's directory tree and reference it directly
- Publish the shared capsule to the registry and reference it remotely instead
Example fix
// before source = "../shared/capsule.capsule" // after source = "vendor/shared/capsule.capsule"
Defensive patterns
Strategy: validation
Validate before calling
let p = std::path::Path::new(source);
if p.components().any(|c| c == std::path::Component::ParentDir) {
anyhow::bail!("source must stay inside the Distro.toml directory");
} Prevention
- Author capsule sources as clean relative paths under the manifest directory
- Never use `..` segments in Distro.toml source fields
- Lint manifests for ParentDir components in CI
When it happens
Trigger: A Distro.toml capsule source like `../shared/capsule.capsule` or `a/../../escape.capsule`; source_path contains Component::ParentDir.
Common situations: Hand-written manifests referencing shared capsules in a sibling directory via `..`; copy-pasting relative paths from another project layout.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- cache contains a redirect or special entry
- capsule archive contains an unsafe path
- capsule identity or version changed after authority decision
- capsule manifest changed after authority decision
- capsule projection escaped its root
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/fe759605cf5962ce.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/distro/local_source.rs:68
"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()
.with_context(|| format!("failed to resolve local capsule source {source:?}"))?;
if !canonical_path.starts_with(&canonical_root) {
bail!("local capsule source {source:?} escapes the authenticated Distro.toml directory");
}
let metadata = std::fs::metadata(&canonical_path)
.with_context(|| format!("failed to stat local capsule source {source:?}"))?;
if !metadata.is_file() {
bail!("local capsule source {source:?} is not a regular file");
}
if metadata.len() > MAX_MEMBER_BYTES {
bail!(View on GitHub (pinned to affd8760f4)