{"record":{"id":"350ad507f8a8fd85","repo":"jdx/mise","slug":"expose-must-contain-named-python-package-requirements","errorCode":null,"errorMessage":"expose must contain named Python package requirements","messagePattern":"expose must contain named Python package requirements","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/backend/pipx.rs","lineNumber":111,"sourceCode":"            bail!(\"{key} cannot contain empty values\");\n        }\n        Ok(values)\n    }\n\n    fn with(&self) -> Result<Vec<String>> {\n        self.string_list(\"with\")\n    }\n\n    fn expose(&self) -> Result<Vec<String>> {\n        self.string_list(\"expose\")\n    }\n\n    fn exposed_package_names(&self) -> Result<Vec<String>> {\n        self.expose()?\n            .into_iter()\n            .map(|requirement| {\n                let Some(name) = Self::requirement_package_name(&requirement) else {\n                    bail!(\"expose must contain named Python package requirements\");\n                };\n                Ok(name.to_string())\n            })\n            .collect()\n    }\n\n    fn requirement_package_name(requirement: &str) -> Option<&str> {\n        let name = requirement\n            .trim()\n            .split(|c: char| !(c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.')))\n            .next()\n            .unwrap_or_default();\n        (!name.is_empty()).then_some(name)\n    }\n\n    fn dependency_prereleases(&self) -> Result<Option<&'a str>> {\n        let value = match self.values.raw().opts.get(\"dependency_prereleases\") {\n            Some(toml::Value::String(value)) => Some(value.as_str()),","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/backend/pipx.rs#L93-L129","documentation":"The pipx `expose` option lists Python package requirements whose executables are exposed as shims. Each entry must include a package name that requirement_package_name can parse; if an entry is not a recognizable named requirement (e.g. empty or malformed spec), mise cannot derive a package name and fails validation of the tool config.","triggerScenarios":"validate_semantic → exposed_package_names processes an expose entry that requirement_package_name cannot parse into a name — e.g. `expose = [\"==1.0\"]` or an entry with no package identifier.","commonSituations":"Typo'd requirement specifiers in expose; copying a bare version constraint; entries like local paths or URLs the parser doesn't recognize; editing expose entries by hand.","solutions":["Ensure every expose entry starts with a package name: `expose = [\"ruff==0.4.1\"]`","Remove malformed entries that aren't named requirements","Use standard pip requirement syntax (name[extras]specifier)"],"exampleFix":"# before\n[tools]\nlint = { source = \"pipx\", expose = [\"==1.0\"] }\n# after\n[tools]\nlint = { source = \"pipx\", expose = [\"ruff==0.4.1\"] }","handlingStrategy":"validation","validationCode":"// Every expose entry must start with a package name\nconst validRequirement = (r: string) => /^[A-Za-z0-9._-]+(\\[[^\\]]*\\])?([=<>!~].*)?$/.test(r);","typeGuard":"const isNamedRequirement = (r: string) => r.trim() !== '' && /^[A-Za-z0-9._-]+/.test(r.trim());","tryCatchPattern":null,"preventionTips":["Follow standard pip requirement syntax (name first)","Never write bare specifiers like \"==1.0\" in expose","Test `mise install` after editing expose entries"],"tags":["pipx","config","validation","requirements"],"backgroundTag":"invalid-identifier-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"}