jdx/mise · error
exposes no executable scripts
Error message
{} exposes no executable scripts What it means
After installing the uv-locked environment, install_uv_lock runs a Python discovery script that lists the console/gui scripts and script files exposed by the requested packages. If the resulting set is empty, the tool provides nothing to symlink into bin/, so mise fails the install rather than producing an install with no usable executables.
Solutions
- Verify the PyPI package actually ships a CLI; if it's a library, don't install it as a tool (use it as a dependency instead).
- Run `mise lock --bump <tool>` to rebuild the lock with the correct root package and version.
- Check the package's metadata for console_scripts entry points (e.g. inspect the wheel's entry_points.txt).
- If the CLI moved to a differently-named distribution, point the tool at the correct PyPI name.
Example fix
// before: library without entry points installed as a tool [tools] someclilib = "pipx:someclilib" # no console_scripts // after: use the actual CLI distribution [tools] someclilib = "pipx:somecli"
Defensive patterns
Strategy: validation
Validate before calling
// confirm the package ships console scripts before treating it as a CLI tool
const eps = wheelMetadata('entry_points.txt');
if (!/\[console_scripts\]/.test(eps)) console.warn('package exposes no CLI'); Prevention
- Only install PyPI packages that document a CLI as tools
- Check entry_points.txt / docs for console_scripts before adding a pipx tool
- Use libraries as dependencies, not installable tools
When it happens
Trigger: Thrown from install_uv_lock (called by install_version_) when the JSON returned by the DISCOVER_SCRIPTS python snippet deserializes to an empty list — i.e. none of the locked packages expose console_scripts/gui_scripts entry points or script files in the venv scripts directory.
Common situations: Locking/installing a library-only PyPI package (no CLI) as a tool; all entry points gated behind inactive environment markers; the wrong package name expected by importlib.metadata (dependency split/renamed); installing via `pipx:` when the CLI actually lives in a different distribution.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- has no uv dependency graph; run `mise lock
- invalid Python entry point name
- has a wheel without a SHA256 hash
- no precompiled python found for
- pipx was found during dependency validation but could not…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/ff566e00c7f91e90.
Report an issue: GitHub.
Appendix: source
Thrown at src/backend/pipx/lock.rs:494
.execute()?;
let scripts = project
.join(".venv")
.join(if cfg!(windows) { "Scripts" } else { "bin" });
let python = scripts.join(if cfg!(windows) {
"python.exe"
} 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"))View on GitHub (pinned to 533346cc37)