jdx/mise · error

invalid Python entry point name

Error message

invalid Python entry point name

What it means

Each discovered executable name from the installed environment is validated with is_plain_file_name before being symlinked into the install's bin/ directory. Names that are not plain file names (e.g. containing path separators, `..`, or other traversal-capable characters) are rejected to prevent a malicious or malformed package metadata from writing outside the install path.

Solutions

  1. Remove the offending package from your dependency set and re-lock: `mise lock --bump <tool>`.
  2. Inspect the installed distribution's entry_points.txt / RECORD to find the malformed entry point name.
  3. Report the package to PyPI/maintainers if the entry point name is genuinely malicious; treat the package as untrusted.
  4. Install a known-good version of the package that does not declare pathological entry point names.

Example fix

// before (entry_points.txt from malicious wheel)
[console_scripts]
../../evil = pkg:main

// after: pin a clean version and re-lock
$ mise lock --bump <tool>  # against a vetted version
Defensive patterns

Strategy: validation

Validate before calling

const names = discoverScripts();
if (names.some(n => /[\/\\]/.test(n) || n === '..' || n === '.')) throw new Error('suspicious entry point name');

Prevention

When it happens

Trigger: Thrown from install_uv_lock when a name in the DISCOVER_SCRIPTS JSON output fails crate::file::is_plain_file_name — e.g. an entry point name containing `/`, `\\`, or other non-plain-file characters, typically from a crafted or corrupted distribution's entry_points metadata.

Common situations: A compromised or malicious PyPI package declares entry points with path-like names; corrupted metadata inside an installed wheel; unusual distributions with exotic entry point names.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

Thrown at src/backend/pipx/lock.rs:500

        } else {
            "python"
        });
        let packages = self.locked_entry_point_packages(tv, lock)?;
        let packages = serde_json::to_string(&packages)?;
        let names = CmdLineRunner::new(python)
            .args(["-I", "-c", DISCOVER_SCRIPTS, &packages])
            .arg(&scripts)
            .read()
            .await?;
        let names: Vec<String> = serde_json::from_str(names.trim())?;
        if names.is_empty() {
            bail!("{} exposes no executable scripts", self.ba.short);
        }
        let bin = tv.install_path().join("bin");
        crate::file::create_dir_all(&bin)?;
        for name in names {
            if !crate::file::is_plain_file_name(&name) {
                bail!("invalid Python entry point name");
            }
            crate::file::make_symlink_or_copy(&scripts.join(&name), &bin.join(&name))?;
        }
        Ok(())
    }

    fn locked_entry_point_packages(&self, tv: &ToolVersion, lock: &UvLock) -> Result<Vec<String>> {
        let root_requirement = lock
            .project
            .get("project")
            .and_then(toml::Value::as_table)
            .and_then(|project| project.get("dependencies"))
            .and_then(toml::Value::as_array)
            .and_then(|dependencies| dependencies.first())
            .and_then(toml::Value::as_str)
            .ok_or_else(|| eyre!("missing locked Python root requirement"))?;
        let root = PipxOptions::requirement_package_name(root_requirement)
            .ok_or_else(|| eyre!("invalid locked Python root requirement"))?;

View on GitHub (pinned to 533346cc37)