{"record":{"id":"a99812b1df994835","repo":"astrid-runtime/astrid","slug":"source-produced-capsule-archives-but-no-capsul","errorCode":null,"errorMessage":"source produced {} .capsule archives but no capsule name to pick one; expected an archive named '<capsule>.capsule'","messagePattern":"source produced (.+?) \\.capsule archives but no capsule name to pick one; expected an archive named '<capsule>\\.capsule'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/github_source.rs","lineNumber":127,"sourceCode":"        .collect()\n}\n\n/// Choose which `.capsule` to install when a source yields several. A monorepo\n/// builds/releases one archive per capsule crate, named `<capsule>.capsule`, so\n/// picking \"the first\" would install the wrong one. Returns the index into\n/// `names` of the chosen archive.\n///\n/// * none        -> `Ok(None)` (caller falls back, e.g. release -> clone+build).\n/// * exactly one -> `Ok(Some(0))` — unambiguous; `name_hint` is irrelevant.\n/// * several     -> the one named `<name_hint>.capsule`. Without a hint, or with\n///   no matching name, refuse rather than silently install the wrong capsule.\npub fn pick_capsule(names: &[&str], name_hint: Option<&str>) -> anyhow::Result<Option<usize>> {\n    match names {\n        [] => Ok(None),\n        [_] => Ok(Some(0)),\n        many => {\n            let Some(hint) = name_hint else {\n                bail!(\n                    \"source produced {} .capsule archives but no capsule name to pick one; \\\n                     expected an archive named '<capsule>.capsule'\",\n                    many.len()\n                );\n            };\n            // Match the hint against each candidate's stem via `strip_suffix`\n            // (no per-call allocation) rather than `format!`-ing the target.\n            match many\n                .iter()\n                .position(|n| n.strip_suffix(\".capsule\") == Some(hint))\n            {\n                Some(idx) => Ok(Some(idx)),\n                None => bail!(\n                    \"no '.capsule' archive named '{hint}.capsule' among [{}]\",\n                    many.join(\", \")\n                ),\n            }\n        },","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/github_source.rs#L109-L145","documentation":"After a GitHub source build produces .capsule archives, pick_capsule must select one. With a single archive it picks it; with several, it needs a name hint (the capsule name) to disambiguate. If multiple archives were produced and name_hint is None, it cannot choose and errors instead of guessing.","triggerScenarios":"Calling pick_capsule (via install_from_github, resolve_capsule_to_file, or clone_and_build) with names.len() > 1 and name_hint = None — i.e. the build emitted two or more .capsule files and no capsule name was supplied to match against archive stems.","commonSituations":"A monorepo GitHub repo whose build produces multiple .capsule artifacts while the user ran an install command without naming the capsule; a script calling resolve_capsule_to_file without passing the name; a renamed capsule so the expected `<name>.capsule` convention no longer matches the configured name.","solutions":["Re-run the install with the capsule name so name_hint is Some and can disambiguate the archives (e.g. `install owner/repo <capsule-name>`).","Delete extra .capsule artifacts from the build output so only one archive remains, or build only the target capsule.","Rename the produced archives so exactly one follows the `<capsule>.capsule` convention and re-run."],"exampleFix":"// before\nresolve_capsule_to_file(&source, None)?;\n// after\nresolve_capsule_to_file(&source, Some(\"my-capsule\"))?;","handlingStrategy":"validation","validationCode":"// caller-side check before resolving:\nif capsule_names.len() > 1 && name_hint.is_none() {\n    return Err(anyhow::anyhow!(\n        \"multiple .capsule archives produced ({:?}); pass the capsule name\", capsule_names));\n}","typeGuard":"fn can_pick(names: &[&str], hint: Option<&str>) -> bool {\n    match names.len() { 0 | 1 => true, _ => hint.is_some() }\n}","tryCatchPattern":"match pick_capsule(&names, hint) {\n    Err(e) if e.to_string().contains(\"no capsule name to pick\") => {\n        eprintln!(\"build produced {} archives; re-run with the capsule name\", names.len());\n    }\n    other => other?,\n}","preventionTips":["Always pass the capsule name when installing from multi-artifact repos.","Keep one .capsule artifact per CI build or name artifacts `<capsule>.capsule`.","Fail fast in scripts when name_hint would be None with multiple candidates."],"tags":["rust","github","ambiguity","naming"],"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-14T11:17:12.474Z"}