jdx/mise · error

must be a string or array of strings

Error message

{key} must be a string or array of strings

What it means

The pipx backend's string_list helper parses backend options such as `with` and `expose`, which must each be a single string or an array of strings. If the TOML option is another type (integer, boolean, table) or an array containing non-strings, mise raises this error naming the offending key.

Solutions

  1. Quote string values: `with = "pytest"`
  2. Ensure arrays contain only quoted strings: `expose = ["black", "ruff"]`
  3. Remove the malformed option if it is not needed
  4. Run `mise settings` / docs to confirm the option's expected type

Example fix

# before
[tools]
ruff = { source = "pipx", with = [1, 2] }
# after
[tools]
ruff = { source = "pipx", with = ["black"] }
Defensive patterns

Strategy: validation

Validate before calling

# Validate tool config values are strings or arrays of strings
jq -e '.tools | to_entries | map(.value | (type == "string" or ((type == "object") and ((.with // []) | type == "array")))) | all' mise.toml 2>/dev/null || cat mise.toml

Type guard

const isStringList = (v: unknown): v is string | string[] =>
  typeof v === 'string' || (Array.isArray(v) && v.every(x => typeof x === 'string'));

Prevention

When it happens

Trigger: A `[tools]` entry like `foo = { source = "pipx", with = 123 }` or `expose = [true, "pkg"]` — string_list receives a TOML value that is neither a string nor an array of strings.

Common situations: Unquoted numeric or boolean values in mise.toml; a table passed where a list is expected; copy-paste from docs with wrong syntax; tool-versions/lockfile opts carrying non-string values.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at src/backend/pipx.rs:90

        let values = match value {
            toml::Value::String(value) => {
                if value.trim_start().starts_with('[') {
                    serde_json::from_str(value)
                        .map_err(|_| eyre!("{key} must be a string or array of strings"))?
                } else {
                    vec![value.clone()]
                }
            }
            toml::Value::Array(values) => values
                .iter()
                .map(|value| {
                    value
                        .as_str()
                        .map(str::to_owned)
                        .ok_or_else(|| eyre!("{key} must be a string or array of strings"))
                })
                .collect::<Result<Vec<_>>>()?,
            _ => bail!("{key} must be a string or array of strings"),
        };
        if values.iter().any(|value| value.trim().is_empty()) {
            bail!("{key} cannot contain empty values");
        }
        Ok(values)
    }

    fn with(&self) -> Result<Vec<String>> {
        self.string_list("with")
    }

    fn expose(&self) -> Result<Vec<String>> {
        self.string_list("expose")
    }

    fn exposed_package_names(&self) -> Result<Vec<String>> {
        self.expose()?
            .into_iter()

View on GitHub (pinned to 533346cc37)