{"record":{"id":"e9b0e322223fadfb","repo":"jdx/mise","slug":"key-must-be-a-string-or-array-of-strings","errorCode":null,"errorMessage":"{key} must be a string or array of strings","messagePattern":"(.+?) must be a string or array of strings","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/backend/pipx.rs","lineNumber":90,"sourceCode":"        let values = match value {\n            toml::Value::String(value) => {\n                if value.trim_start().starts_with('[') {\n                    serde_json::from_str(value)\n                        .map_err(|_| eyre!(\"{key} must be a string or array of strings\"))?\n                } else {\n                    vec![value.clone()]\n                }\n            }\n            toml::Value::Array(values) => values\n                .iter()\n                .map(|value| {\n                    value\n                        .as_str()\n                        .map(str::to_owned)\n                        .ok_or_else(|| eyre!(\"{key} must be a string or array of strings\"))\n                })\n                .collect::<Result<Vec<_>>>()?,\n            _ => bail!(\"{key} must be a string or array of strings\"),\n        };\n        if values.iter().any(|value| value.trim().is_empty()) {\n            bail!(\"{key} cannot contain empty values\");\n        }\n        Ok(values)\n    }\n\n    fn with(&self) -> Result<Vec<String>> {\n        self.string_list(\"with\")\n    }\n\n    fn expose(&self) -> Result<Vec<String>> {\n        self.string_list(\"expose\")\n    }\n\n    fn exposed_package_names(&self) -> Result<Vec<String>> {\n        self.expose()?\n            .into_iter()","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/backend/pipx.rs#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Quote string values: `with = \"pytest\"`","Ensure arrays contain only quoted strings: `expose = [\"black\", \"ruff\"]`","Remove the malformed option if it is not needed","Run `mise settings` / docs to confirm the option's expected type"],"exampleFix":"# before\n[tools]\nruff = { source = \"pipx\", with = [1, 2] }\n# after\n[tools]\nruff = { source = \"pipx\", with = [\"black\"] }","handlingStrategy":"validation","validationCode":"# Validate tool config values are strings or arrays of strings\njq -e '.tools | to_entries | map(.value | (type == \"string\" or ((type == \"object\") and ((.with // []) | type == \"array\")))) | all' mise.toml 2>/dev/null || cat mise.toml","typeGuard":"const isStringList = (v: unknown): v is string | string[] =>\n  typeof v === 'string' || (Array.isArray(v) && v.every(x => typeof x === 'string'));","tryCatchPattern":null,"preventionTips":["Always quote string values in mise.toml backend options","Keep arrays homogeneous (all strings)","Validate mise.toml against the mise schema (mise run render:schema / JSON schema in editors)"],"tags":["pipx","config","type-error","mise-toml"],"backgroundTag":"invalid-argument-format","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}