{"record":{"id":"91dc84449a0dd5bd","repo":"astrid-runtime/astrid","slug":"distro-capsule-expected-has-no-concrete-releas","errorCode":null,"errorMessage":"distro capsule '{expected}' has no concrete released version or tag","messagePattern":"distro capsule '(.+?)' has no concrete released version or tag","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/install_batch.rs","lineNumber":52,"sourceCode":"    /// sent. This lets init avoid treating a resume as a newly landed grant.\n    pub(crate) skipped: bool,\n}\n\n#[derive(Debug)]\npub(crate) struct BatchInstallOutcome {\n    pub(crate) installed: Vec<InstalledCapsuleOutcome>,\n    pub(crate) resolved_ref: Option<String>,\n}\n\n/// Install without prompting, returning the identities that actually landed.\npub(crate) async fn install_capsule_batch(\n    source: &str,\n    expected: &CapsuleId,\n    workspace: bool,\n    refspec: &RefSpec,\n    principal: &astrid_core::PrincipalId,\n) -> anyhow::Result<BatchInstallOutcome> {\n    anyhow::ensure!(\n        refspec.version.is_some() || refspec.tag.is_some(),\n        \"distro capsule '{expected}' has no concrete released version or tag\"\n    );\n    super::install::BATCH_MODE.store(true, Ordering::Relaxed);\n    let prompt = super::install::ManualInstallOptions::default();\n    let result = super::install::install_capsule_inner(\n        source,\n        Some(expected.as_str()),\n        workspace,\n        refspec,\n        principal,\n        Some(expected),\n        &prompt,\n    )\n    .await;\n    super::install::BATCH_MODE.store(false, Ordering::Relaxed);\n    result.map(|(installed, resolved_ref)| BatchInstallOutcome {\n        installed,","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/install_batch.rs#L34-L70","documentation":"Batch/distro capsule installs must pin an exact released version or tag; `install_capsule_batch` refuses to run with an unpinned refspec because distro capsules must be reproducibly resolvable. It throws when `refspec.version` and `refspec.tag` are both `None`.","triggerScenarios":"Calling `install_capsule_batch` with a `RefSpec` that has neither `version` nor `tag` set — e.g. installing latest/moving-head for a distro capsule, or a refspec built only from a branch name or digest-less default.","commonSituations":"Automated scripts omitting `--version`/`--tag` when batch-installing distro capsules; users expecting 'latest' semantics that batch mode deliberately forbids; migration tooling copying refspecs from interactive installs that allowed floating refs.","solutions":["Pass an explicit version, e.g. `--version 1.2.3`","Pass an explicit tag, e.g. `--tag v1.2.3`","If you need latest behavior, resolve the latest version yourself (query the GitHub release) then pass it as the version"],"exampleFix":"// before\ninstall_batch(\"@acme/distro\", RefSpec { version: None, tag: None, .. })\n// after\ninstall_batch(\"@acme/distro\", RefSpec { version: Some(\"1.2.3\"), tag: None, .. })","handlingStrategy":"validation","validationCode":"fn refspec_is_pinned(r: &RefSpec) -> bool {\n    r.version.is_some() || r.tag.is_some()\n}\nassert!(refspec_is_pinned(&refspec), \"batch install requires --version or --tag\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass --version or --tag for batch/distro installs","Resolve 'latest' yourself via the GitHub API then pass the concrete version","Store pinned versions in lockfiles/automation configs"],"tags":["capsule","versioning","cli","batch-install"],"backgroundTag":"missing-required-argument","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}