{"record":{"id":"c96349a6eeb739e6","repo":"jdx/mise","slug":"packslip-plugin-version-must-not-be-empty","errorCode":null,"errorMessage":"packslip plugin version must not be empty","messagePattern":"packslip plugin version must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugins/packslip.rs","lineNumber":37,"sourceCode":"\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub(crate) struct Source {\n    project: String,\n    request: String,\n}\n\nimpl Source {\n    pub(crate) fn parse(value: &str) -> Result<Option<Self>> {\n        let Some(value) = value.strip_prefix(\"packslip:\") else {\n            return Ok(None);\n        };\n        let (project, request) = value.split_once('#').unwrap_or((value, \"latest\"));\n        let project = project_name(project)?;\n        ensure!(\n            project.starts_with(\"github.com/\") && packslip::model::repository(&project).is_some(),\n            \"packslip plugin sources currently require a GitHub repository\"\n        );\n        ensure!(\n            !request.is_empty(),\n            \"packslip plugin version must not be empty\"\n        );\n        Ok(Some(Self {\n            project,\n            request: request.to_owned(),\n        }))\n    }\n\n    pub(crate) fn url(&self) -> String {\n        format!(\"packslip:{}#{}\", self.project, self.request)\n    }\n\n    pub(crate) fn with_version(mut self, version: Option<String>) -> Self {\n        if let Some(version) = version {\n            self.request = version;\n        }\n        self","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/plugins/packslip.rs#L19-L55","documentation":"After splitting a packslip source on `#` (defaulting the request to \"latest\"), parse in src/plugins/packslip.rs rejects an empty version request. An empty request can only occur with a trailing/empty fragment like `packslip:github.com/owner/repo#`, so the guard keeps malformed specs out of the toolset.","triggerScenarios":"A source string ending in `#` with nothing after it — e.g. `mise use 'packslip:github.com/owner/repo#'` or `[tools] x = \"packslip:github.com/owner/repo#\"` in mise.toml, or a template/variable that interpolated to an empty version.","commonSituations":"A script substitutes `${VERSION}` that is unset, leaving `repo#${VERSION}` empty; a hand-edited mise.toml left a dangling `#`; copy-paste truncation of a pinned version.","solutions":["Remove the trailing `#` to use the default `latest` request: `packslip:github.com/owner/repo`.","Pin an actual version after the `#`: `packslip:github.com/owner/repo#1.2.3`.","Fix the script/template so the version variable is non-empty before substitution."],"exampleFix":"// before\n[tools]\ngh = \"packslip:github.com/cli/cli#\"\n\n// after\n[tools]\ngh = \"packslip:github.com/cli/cli\"","handlingStrategy":"validation","validationCode":"// ensure version fragment is non-empty before building the spec\n[ -n \"${VERSION:-}\" ] || unset VERSION\nspec=\"packslip:github.com/owner/repo${VERSION:+#${VERSION}}\"","typeGuard":"fn has_version_request(src: &str) -> bool {\n    match src.split_once('#') {\n        Some((_, req)) => !req.is_empty(),\n        None => true,\n    }\n}","tryCatchPattern":null,"preventionTips":["Don't append '#' with no version; omit it to default to latest.","Fail fast in scripts when a VERSION variable is unset instead of interpolating empty.","Review hand-edited mise.toml entries for dangling '#' characters."],"tags":["packslip","plugin","version","empty-value"],"backgroundTag":"empty-required-field","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}