zed-industries/zed · error
npm package {name} declares no executable
Error message
npm package {name} declares no executable What it means
When resolving a package's executable, NodeRuntime reads the npm package's package.json `bin` field. The `Bin` enum distinguishes a single string bin, a named map (`Bin::Named`), and no bin at all (`None`). This message is the None case: the installed package literally declares no executable, so no binary path can be returned.
Source
Thrown at crates/node_runtime/src/node_runtime.rs:1061
file.read_to_string(&mut contents).await?;
let package_json: PackageJson = serde_json::from_str(&contents)
.with_context(|| format!("parsing {}", package_json_path.display()))?;
let relative_path = match package_json.bin {
Some(Bin::Path(path)) => path,
Some(Bin::Named(bins)) => {
let unscoped_name = name.rsplit('/').next().unwrap_or(name);
let path = if bins.len() == 1 {
bins.values().next()
} else {
bins.get(unscoped_name)
};
path.with_context(|| {
format!("npm package {name} declares no executable named {unscoped_name}")
})?
.clone()
}
None => bail!("npm package {name} declares no executable"),
};
Ok(package_directory.join(relative_path))
}
#[derive(Clone)]
pub struct UnavailableNodeRuntime {
error_message: Arc<String>,
}
#[async_trait::async_trait]
impl NodeRuntimeTrait for UnavailableNodeRuntime {
fn boxed_clone(&self) -> Box<dyn NodeRuntimeTrait> {
Box::new(self.clone())
}
fn binary_path(&self) -> Result<PathBuf> {
bail!("{}", self.error_message)
}View on GitHub (pinned to 9d272b0363)
Solutions
- Verify the package actually ships a CLI: `npm view <name> bin` — if empty, you want a different package
- Request the correct CLI package name for the tool
- Pin a version known to declare the bin if a release dropped it
Example fix
# before: package declares no bin
$ npm view some-parser bin
{}
# after: request the package that ships the CLI
$ npm view some-parser-cli bin
{ 'some-parser-cli': './bin/cli.js' } Defensive patterns
Strategy: validation
Validate before calling
async fn package_has_bin(name: &str, version: &str, http: &Arc<dyn HttpClient>) -> bool {
let meta = fetch_package_json(name, version, http).await;
meta.get("bin").map_or(false, |b| !b.is_null())
} Type guard
fn declares_executable(package_json: &serde_json::Value) -> bool {
package_json.get("bin").is_some_and(|b| {
b.is_string() || b.as_object().is_some_and(|m| !m.is_empty())
})
} Try / catch
match resolve_executable_path(&package) {
Err(e) if e.to_string().contains("declares no executable") => {
// pick the CLI package variant or pin a version that shipped a bin
pick_cli_package_alternative(&package).await
}
r => r?,
} Prevention
- Check `npm view <pkg> bin` before wiring a package as a tool
- Name the CLI package, not its library sibling, in configuration
- Pin versions whose bin layout you have verified
When it happens
Trigger: Calling the package-install/exec path (e.g. installing a language server or tool) for an npm package whose package.json has no `bin` field — typically a pure library package, or the wrong package was requested.
Common situations: Asking for the library package instead of the CLI package (e.g. a `-lib`/core package vs the bin-publishing one); a package version that temporarily dropped its bin declaration; typos in package names resolving to a library.
Related errors
- GitHub Copilot native language server binary was not install
- no version found for npm package {package_name} before {befo
- run request is missing a 'benchmark' block
- registry dataset requires a name
- provide a run_id or --job-dir
AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20).
Data as JSON: /api/errors/1b53ab0640808987.
Report an issue: GitHub.