astrid-runtime/astrid · error

capsule ' ' is missing from the shuttle mirror

Error message

capsule '{}' is missing from the shuttle mirror

What it means

verify_capsule_hashes iterates the capsules recorded in DistroLock and resolves each to its mirror file. If a locked capsule's archive is absent from the shuttle mirror, the integrity gate bails before any install side effects (the function is deliberately pure). Unlike error 290 this checks the full lock, not just the user's selection.

Solutions

  1. Rebuild the shuttle so the mirror contains every capsule named in DistroLock
  2. Verify the mirror directory contents against the lock's capsule list before transport
  3. Re-copy the shuttle if media loss is suspected

Example fix

// before
// ls /media/shuttle/capsules -> distro-core.capsule missing
// after
// astrid distro pack --output /media/shuttle  # repopulate mirror to match lock
Defensive patterns

Strategy: validation

Validate before calling

for entry in &lock.capsules {
    let file = shuttle::capsule_mirror_path(mirror, &entry.name);
    if !file.is_file() {
        return Err(anyhow!("capsule '{}' missing from mirror", entry.name));
    }
}

Prevention

When it happens

Trigger: install_from_shuttle where the lock lists a capsule whose archive file is missing from the mirror directory — shuttle built incompletely, file deleted, or lock from a newer build than the mirror.

Common situations: Shuttle media partially copied; capsules dropped during packing due to disk-full; lock regenerated while the mirror directory wasn't repopulated.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-cli/src/commands/distro/shuttle_install.rs:302

        },
        None if signed => bail!(
            "signed shuttle for '{distro_id}' is missing its manifest_hash binding — refusing. \
             The signature covers the lock, not Distro.toml; without manifest_hash the manifest \
             (env/selection) is unauthenticated and could be swapped."
        ),
        None => Ok(()),
    }
}

/// Verify the per-capsule blake3 of every lock entry against the bytes
/// actually present in the mirror. Returns an error on the first
/// mismatch or missing file. Pure (no install side effects) so the
/// integrity gate is unit-testable.
fn verify_capsule_hashes(mirror: &Path, lock: &DistroLock) -> anyhow::Result<()> {
    for entry in &lock.capsules {
        let file = shuttle::capsule_mirror_path(mirror, &entry.name);
        if !file.is_file() {
            bail!(
                "capsule '{}' is missing from the shuttle mirror",
                entry.name
            );
        }
        let bytes = std::fs::read(&file)
            .with_context(|| format!("failed to read mirrored capsule {}", entry.name))?;
        let actual = format!("blake3:{}", blake3::hash(&bytes).to_hex());
        if entry.hash != actual {
            bail!(
                "capsule '{}' hash mismatch: lock has {}, archive has {actual}",
                entry.name,
                entry.hash
            );
        }
    }
    Ok(())
}

View on GitHub (pinned to affd8760f4)