jdx/mise · error

backend must be a string or table

Error message

backend must be a string or table

What it means

In parse_registry_backend, each element of a registry entry's backends array must be either a plain string (shorthand like "aqua:owner/repo") or a TOML table with full/platforms/options fields. Any other TOML type — integer, float, boolean, array, or datetime — falls through to this bail and the registry fails to load.

Source

Thrown at src/registry.rs:502

                .map(|options| {
                    options
                        .iter()
                        .map(|(key, value)| {
                            let mut serialized = String::new();
                            value.serialize(toml::ser::ValueSerializer::new(&mut serialized))?;
                            Ok((leak_string(key.clone()), leak_string(serialized)))
                        })
                        .collect::<Result<Vec<_>>>()
                })
                .transpose()?
                .unwrap_or_default();
            Ok(RegistryBackend {
                full: leak_string(full.to_string()),
                platforms: leak_vec(platforms),
                options: leak_vec(options),
            })
        }
        _ => bail!("backend must be a string or table"),
    }
}

fn parse_registry_test(value: &toml::Value) -> Result<RegistryToolTest> {
    let table = value
        .as_table()
        .ok_or_else(|| eyre::eyre!("test must be a table"))?;
    let cmd = table
        .get("cmd")
        .and_then(toml::Value::as_str)
        .ok_or_else(|| eyre::eyre!("test.cmd must be a string"))?;
    let expected = table
        .get("expected")
        .and_then(toml::Value::as_str)
        .ok_or_else(|| eyre::eyre!("test.expected must be a string"))?;
    let tools = string_array(table.get("tools"), "test.tools")?;
    Ok(RegistryToolTest {
        cmd: leak_string(cmd.to_string()),

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Use the string form for simple cases: backends = ["aqua:cli/cli"].
  2. Use the table form only when you need full/platforms/options: backends = [{ full = "aqua:cli/cli", platforms = ["linux", "macos"] }].
  3. Ensure the backends array itself is non-empty — an empty array is rejected separately just above this code.
  4. Validate with the registry test suite (`cargo test registry`) before submitting.

Example fix

# before
backends = [["aqua:cli/cli"]]
# after
backends = ["aqua:cli/cli"]
Defensive patterns

Strategy: validation

Validate before calling

# every backends[] element must be a string or a table
python3 - <<'EOF'
import sys, tomllib
cfg = tomllib.load(open('registry/mytool.toml','rb'))
for b in cfg['tools']['mytool']['backends']:
    if not isinstance(b, (str, dict)):
        sys.exit(f'backend must be a string or table, got: {b!r}')
EOF

Type guard

def is_registry_backend(v) -> bool: return isinstance(v, (str, dict))

Prevention

When it happens

Trigger: Writing backends = [123], backends = [true], backends = [["aqua:owner/repo"]], or backends = [1979-05-27] in a registry/*.toml entry. Also triggered by YAML-to-TOML conversions that turn a single backend string into a nested array.

Common situations: Contributing a registry entry and wrapping the backend in extra brackets; automated tooling that emits a non-string scalar; merging registry entries by hand and leaving a stray value.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/6db639cfe029f835. Report an issue: GitHub.