jdx/mise · error

expose must contain named Python package requirements

Error message

expose must contain named Python package requirements

What it means

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.

Solutions

  1. Ensure every expose entry starts with a package name: `expose = ["ruff==0.4.1"]`
  2. Remove malformed entries that aren't named requirements
  3. Use standard pip requirement syntax (name[extras]specifier)

Example fix

# before
[tools]
lint = { source = "pipx", expose = ["==1.0"] }
# after
[tools]
lint = { source = "pipx", expose = ["ruff==0.4.1"] }
Defensive patterns

Strategy: validation

Validate before calling

// Every expose entry must start with a package name
const validRequirement = (r: string) => /^[A-Za-z0-9._-]+(\[[^\]]*\])?([=<>!~].*)?$/.test(r);

Type guard

const isNamedRequirement = (r: string) => r.trim() !== '' && /^[A-Za-z0-9._-]+/.test(r.trim());

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/350ad507f8a8fd85. Report an issue: GitHub.

Appendix: source

Thrown at src/backend/pipx.rs:111

            bail!("{key} cannot contain empty values");
        }
        Ok(values)
    }

    fn with(&self) -> Result<Vec<String>> {
        self.string_list("with")
    }

    fn expose(&self) -> Result<Vec<String>> {
        self.string_list("expose")
    }

    fn exposed_package_names(&self) -> Result<Vec<String>> {
        self.expose()?
            .into_iter()
            .map(|requirement| {
                let Some(name) = Self::requirement_package_name(&requirement) else {
                    bail!("expose must contain named Python package requirements");
                };
                Ok(name.to_string())
            })
            .collect()
    }

    fn requirement_package_name(requirement: &str) -> Option<&str> {
        let name = requirement
            .trim()
            .split(|c: char| !(c.is_ascii_alphanumeric() || matches!(c, '-' | '_' | '.')))
            .next()
            .unwrap_or_default();
        (!name.is_empty()).then_some(name)
    }

    fn dependency_prereleases(&self) -> Result<Option<&'a str>> {
        let value = match self.values.raw().opts.get("dependency_prereleases") {
            Some(toml::Value::String(value)) => Some(value.as_str()),

View on GitHub (pinned to 533346cc37)