jdx/mise · error
backend must be a string or table
Error message
backend must be a string or table
What it means
The registry TOML parser accepts a backend definition either as a plain string (shorthand) or as a table with fields like full/platforms/options. Any other TOML type (array, integer, boolean, etc.) falls into the `_` match arm and bails with this message.
Solutions
- Write the backend as a plain string: `backend = "github:user/repo"`.
- Or use the table form with fields like `backend = { full = "...", platforms = [...] }`.
- Remove stray brackets/quotes around the value and re-run the failing command.
Example fix
// before (registry TOML) backend = ["github:jdx/mise-node"] // after backend = "github:jdx/mise-node"
Defensive patterns
Strategy: validation
Validate before calling
const b = table.get('backend');
if (typeof b !== 'string' && !(b && typeof b === 'object' && !Array.isArray(b)))
throw new Error('backend must be a string or table'); Type guard
const isBackendValue = (v) => typeof v === 'string' || (v !== null && typeof v === 'object' && !Array.isArray(v));
Prevention
- Write backend entries as `backend = "provider:repo"` strings, never arrays.
- Run `mise registry` or a TOML linter after hand-editing registry files.
When it happens
Trigger: A registry entry's backend value written as a non-string, non-table type, e.g. `backend = ["github"]`, `backend = 1`, or `backend = true`, while `parse_backend` pattern-matches.
Common situations: Hand-editing registry/ TOML files and accidentally adding brackets around the backend name; tool-generated TOML emitting arrays for repeated keys; merging duplicate entries that TOML turns into an array.
Related errors
- expected `run` to be a string or array of strings
- expected value of to be a , got
- [history] exclude in
- [history] in is not a table
- {message}
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/6db639cfe029f835.
Report an issue: GitHub.
Appendix: source
Thrown at src/registry.rs:585
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),
min_version,
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 533346cc37)