{"record":{"id":"107003a966f9ff14","repo":"jdx/mise","slug":"invalid-system-package-spec-spec-expected-m","errorCode":null,"errorMessage":"invalid system package spec '{spec}': expected '<manager>:<package>' (e.g. \"apt:curl\")","messagePattern":"invalid system package spec '(.+?)': expected '<manager>:<package>' \\(e\\.g\\. \"apt:curl\"\\)","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/mod.rs","lineNumber":412,"sourceCode":"    BrewCask { adopt: BTreeSet<String> },\n}\n\nimpl ManagerPackageOptions {\n    #[cfg(unix)]\n    pub(crate) fn brew_cask_adopt(&self, name: &str) -> bool {\n        matches!(self, Self::BrewCask { adopt } if adopt.contains(name))\n    }\n}\n\n/// Split a `\"manager:package\"` spec (config key or CLI argument). Only the\n/// first `:` separates — apt arch qualifiers (\"apt:gcc:arm64\") and brew\n/// versioned formula names (\"brew:postgresql@17\") stay part of the package.\npub(crate) fn parse_spec(spec: &str) -> eyre::Result<(String, String)> {\n    match spec.split_once(':') {\n        Some((mgr, pkg)) if !mgr.is_empty() && !pkg.is_empty() => {\n            Ok((mgr.to_string(), pkg.to_string()))\n        }\n        _ => bail!(\n            \"invalid system package spec '{spec}': expected '<manager>:<package>' (e.g. \\\"apt:curl\\\")\"\n        ),\n    }\n}\n\n/// Split a `mise bootstrap packages use` spec `manager:package[@version]` into its parts.\n///\n/// `@version` mirrors `mise use tool@version`; `@latest` (or no `@`) means no\n/// pin. brew and brew-cask are exempt from `@` parsing: `@` is part of\n/// Homebrew names (`postgresql@17` — that name IS brew's versioning\n/// mechanism), and bottles/casks can't be installed at a pinned version\n/// anyway. mas uses numeric ADAM IDs only.\npub(crate) fn parse_use_spec(spec: &str) -> eyre::Result<(String, PackageRequest)> {\n    let (mgr, rest) = parse_spec(spec)?;\n    let rest = normalize_use_spec_package_name(&mgr, &rest)?;\n    validate_package_name(&mgr, rest)?;\n    if is_opaque_package_manager(&mgr) {\n        return Ok((","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/mod.rs#L394-L430","documentation":"parse_spec splits a system package spec like \"apt:curl\" on the first ':'. The input had no valid manager/package split, so it cannot be routed to a package manager; the spec in the config key or CLI argument is the input at fault.","triggerScenarios":"Calling mise bootstrap packages (parse) with a spec like 'curl' (no colon), ':curl' (empty manager), or 'apt:' (empty package).","commonSituations":"Typing a package name without the manager prefix in bootstrap packages config or CLI; copy-pasting specs from docs that only show the package name; TOML entries missing the manager.","solutions":["Add the manager prefix, e.g. apt:curl instead of curl","Check for a stray or missing colon in the spec","Fix the entry in your bootstrap packages config file"],"exampleFix":"// before\npackages = [\"curl\"]\n// after\npackages = [\"apt:curl\"]","handlingStrategy":"validation","validationCode":"const valid = /^[^:\\s]+:[^:\\s]+$/.test(spec); // e.g. \"apt:curl\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always prefix package names with the manager and a colon","Validate specs against /^[^:]+:[^:]+$/ before invoking mise","Keep bootstrap packages config entries in manager:package form","Copy specs from docs including the manager prefix"],"tags":["cli","parsing","system-packages","config"],"backgroundTag":"invalid-argument-format","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}