{"record":{"id":"a9f7cc933b76914e","repo":"jdx/mise","slug":"message-a9f7cc","errorCode":null,"errorMessage":"{message}","messagePattern":"\\{message\\}","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/hooks.rs","lineNumber":125,"sourceCode":"                } else {\n                    Some(Self {\n                        phase,\n                        run,\n                        config_path: config_path.clone(),\n                    })\n                }\n            })\n            .collect();\n        Ok(hooks)\n    }\n}\n\nfn string_array(values: Vec<toml::Value>, message: &str) -> Result<Vec<String>> {\n    let mut out = vec![];\n    for value in values {\n        match value {\n            toml::Value::String(s) => out.push(s),\n            _ => bail!(\"{message}\"),\n        }\n    }\n    Ok(out)\n}\n\npub(crate) async fn run_phase(\n    config: &Config,\n    hooks: &[BootstrapHook],\n    phase: BootstrapHookPhase,\n    dry_run: bool,\n) -> Result<()> {\n    let phase_hooks: Vec<_> = hooks.iter().filter(|hook| hook.phase == phase).collect();\n    if phase_hooks.is_empty() {\n        return Ok(());\n    }\n    info!(\"bootstrap: {phase} hooks\");\n    let shell = Settings::get().default_inline_shell()?;\n    let Some((program, shell_args)) = shell.split_first() else {","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/hooks.rs#L107-L143","documentation":"string_array in src/system/hooks.rs validates that every element of a TOML array given to a hook's `run` is a string, bailing with a caller-supplied message (default: 'expected `run` to contain string commands'). It is raised while expanding a hook defined as an array when at least one element is a non-string TOML value. This keeps hook commands typed as strings so they can be trimmed, templated, and executed.","triggerScenarios":"A hook `run` array containing a non-string element, e.g. run = [\"echo ok\", 42] or run = [\"cmd\", true], passed from from_toml into string_array.","commonSituations":"Accidentally mixing numeric exit codes or booleans into command arrays; programmatic TOML generation emitting unquoted values; copy-paste edits that leave a placeholder like 0 in the list.","solutions":["Quote every element in the run array so all are strings","Remove non-string elements (numbers, booleans, nested tables) from the array","If a command needs a number argument, embed it inside a quoted command string, e.g. run = [\"sleep 2\"]"],"exampleFix":"# before\nrun = [\"echo ok\", 2]\n# after\nrun = [\"echo ok\", \"sleep 2\"]","handlingStrategy":"type-guard","validationCode":"# before loading, ensure every element of run arrays is a string\nbad = [x for x in run_list if not isinstance(x, str)]","typeGuard":"const isStringArray = (v) => Array.isArray(v) && v.every(x => typeof x === 'string');","tryCatchPattern":null,"preventionTips":["Quote all array elements in TOML","Never mix scalars of different types in run arrays","Lint TOML with type-aware tooling"],"tags":["config","toml","type-mismatch"],"backgroundTag":"type-mismatch","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"}