{"record":{"id":"a9f7cc933b76914e","repo":"jdx/mise","slug":"message","errorCode":null,"errorMessage":"{message}","messagePattern":"\\{message\\}","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/hooks.rs","lineNumber":115,"sourceCode":"                let run = run.trim().to_string();\n                if run.is_empty() {\n                    warn!(\"[bootstrap.hooks.{phase}]: empty command, ignoring entry\");\n                    None\n                } else {\n                    Some(Self { phase, run })\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 async fn run_phase(\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 {\n        bail!(\"default inline shell args must not be empty\");","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/hooks.rs#L97-L133","documentation":"string_array validates array-form hook values: every element must be a TOML string. The message is supplied by the caller, so it reads either 'expected string commands' (bare array under [bootstrap.hooks]) or 'expected `run` to contain string commands' (run = [...] in table form). It fires when the array mixes in an integer, float, boolean, table, or nested array.","triggerScenarios":"Writing [bootstrap.hooks] post-tools = [\"npm ci\", 2] or [bootstrap.hooks.final] run = [true, \"echo done\"] — any hook array containing a non-string element.","commonSituations":"Putting numeric options or exit codes into command lists; heterogeneous arrays produced by templating; assuming TOML coerces values like YAML-style tooling does.","solutions":["Quote every element so the array is all strings","Fold numeric flags into the command string itself (\"npm ci --silent\", not [\"npm ci\", 2])","Re-run mise bootstrap to confirm parsing"],"exampleFix":"# before\n[bootstrap.hooks]\npost-tools = [\"npm ci\", 2]\n\n# after\n[bootstrap.hooks]\npost-tools = [\"npm ci --silent\"]","handlingStrategy":"validation","validationCode":"python3 - <<'EOF'\nimport tomllib,sys\nc=tomllib.load(open('mise.toml','rb'))\nfor phase,v in (c.get('bootstrap',{}).get('hooks',{}) or {}).items():\n    items = v if isinstance(v,list) else v.get('run',[]) if isinstance(v,dict) else []\n    if isinstance(items,list) and any(not isinstance(i,str) for i in items):\n        sys.exit(f'hook phase {phase} has non-string array element')\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Quote every element in hook command arrays","Put flags inside the command string (\"npm ci --silent\"), not as separate tokens","Lint hook arrays in CI with a TOML schema check"],"tags":["mise","bootstrap","hooks","toml","type-mismatch"],"backgroundTag":"config-type-mismatch","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}