jdx/mise · error

backends must not be empty

Error message

backends must not be empty

What it means

This error is thrown while parsing a registry tool's TOML table: after reading the 'backends' array (which itself must be present and an array), the loader requires the array to contain at least one backend. A tool declaration with an empty backends list is considered invalid because the tool would have no installable source.

Solutions

  1. Add at least one valid backend entry to the tool's backends array
  2. Remove the tool entry entirely if it should not be in the registry
  3. Check generated/templated registry files for filters that remove all backends

Example fix

// before
[tools.node]
backends = []
// after
[tools.node]
backends = [
  { backend = "core", ... }
]
Defensive patterns

Strategy: validation

Validate before calling

fn validate_backends(table: &toml::Value) -> Result<(), String> {
    let arr = table.get("backends").and_then(|v| v.as_array())
        .ok_or("backends must be a non-empty array")?;
    if arr.is_empty() { return Err("backends must not be empty".into()); }
    Ok(())
}

Type guard

fn has_backends(tool: &toml::Value) -> bool {
    tool.get("backends").and_then(|v| v.as_array()).map_or(false, |a| !a.is_empty())
}

Prevention

When it happens

Trigger: Defining a registry tool with `backends = []` in registry TOML, or constructing the array programmatically (e.g. via templating) such that it ends up empty.

Common situations: Commenting out or deleting all backend entries while leaving the empty array behind; template/CI generation that filters out all backends; copy-pasting a tool entry and removing backends before filling them in.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/registry.rs:418

        for alias in tool.aliases {
            entries.insert((*alias).to_string(), tool.clone());
        }
    }
    Ok(Registry::dynamic(entries, missing_version_order))
}

fn parse_registry_tool(short: &str, value: &toml::Value) -> Result<(RegistryTool, bool)> {
    let table = value
        .as_table()
        .ok_or_else(|| eyre::eyre!("registry tool must be a TOML table"))?;
    let backends = table
        .get("backends")
        .and_then(toml::Value::as_array)
        .ok_or_else(|| eyre::eyre!("backends must be an array"))?
        .iter()
        .map(parse_registry_backend)
        .collect::<Result<Vec<_>>>()?;
    ensure!(!backends.is_empty(), "backends must not be empty");

    let missing_version_order = !table.contains_key("version_order");
    let version_order = match table.get("version_order").and_then(toml::Value::as_str) {
        Some("source") => VersionOrder::Source,
        Some("semver") => VersionOrder::Semver,
        Some(_) => bail!("version_order must be \"source\" or \"semver\""),
        None => VersionOrder::Source,
    };

    ensure!(
        version_order == VersionOrder::Semver || backends.iter().all(|b| b.min_version.is_none()),
        "backend min_version requires version_order = \"semver\""
    );
    let aliases = string_array(table.get("aliases"), "aliases")?;
    let bins = if table.contains_key("bins") {
        string_array(table.get("bins"), "bins")?
    } else {
        BAKED_REGISTRY

View on GitHub (pinned to 533346cc37)