{"record":{"id":"1d0a30c613685878","repo":"jdx/mise","slug":"remote-host-must-set-at-most-one-of-mise-bin","errorCode":null,"errorMessage":"remote host '{}' must set at most one of mise_bin, remote_mise, or bootstrap_command","messagePattern":"remote host '(.+?)' must set at most one of mise_bin, remote_mise, or bootstrap_command","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/remote.rs","lineNumber":271,"sourceCode":"        if let Some(binary) = &self.mise_bin\n            && !binary.is_file()\n        {\n            bail!(\n                \"remote host '{}' mise binary does not exist: {}\",\n                self.name,\n                binary.display()\n            );\n        }\n        let provisioning_strategies = [\n            self.mise_bin.is_some(),\n            self.remote_mise.is_some(),\n            self.bootstrap_command.is_some(),\n        ]\n        .into_iter()\n        .filter(|configured| *configured)\n        .count();\n        if provisioning_strategies > 1 {\n            bail!(\n                \"remote host '{}' must set at most one of mise_bin, remote_mise, or bootstrap_command\",\n                self.name\n            );\n        }\n        for option in &self.ssh_options {\n            validate_value(\"SSH option\", option)?;\n        }\n        for exclude in &self.exclude {\n            validate_value(\"archive exclude\", exclude)?;\n        }\n        if let Some(remote_mise) = &self.remote_mise {\n            validate_remote_executable(remote_mise)?;\n        }\n        if self\n            .bootstrap_command\n            .as_ref()\n            .is_some_and(|command| command.contains('\\0'))\n        {","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/remote.rs#L253-L289","documentation":"Thrown by RemoteHost::validate() when a host defines more than one of the three mutually exclusive mise provisioning strategies: mise_bin (upload a local binary), remote_mise (use an existing remote executable), and bootstrap_command (a shell command that installs mise). mise must pick exactly one strategy per host, so configuring two or three is ambiguous and is rejected before any SSH connection is opened. The message names the offending host from [bootstrap.remote.hosts].","triggerScenarios":"A mise.toml [bootstrap.remote.hosts.<name>] block that sets two of the keys, e.g. remote_mise = \"mise\" together with bootstrap_command = \"curl ... | sh\", or mise_bin plus remote_mise. The check counts configured options in validate(), which runs from hosts_from_config(), ad_hoc_host(), and apply_overrides(). The CLI flags themselves already conflict via clap conflicts_with_all, so this path mostly catches config files and layered config merging.","commonSituations":"Iterating on provisioning: starting with remote_mise, later pasting a bootstrap_command snippet and forgetting to remove the old key; copy-pasting a host template that shows all three keys with two left uncommented; layered config (project mise.toml plus ~/.config/mise/mise.toml) merging extra keys into the same host entry.","solutions":["Open mise.toml, find the host named in the error, and keep exactly one of mise_bin, remote_mise, or bootstrap_command; delete the others","Check layered config files (mise.toml, mise.local.toml, ~/.config/mise/mise.toml, MISE_CONFIG_FILE) since later files merge into the same host entry","If overriding on the command line, pass only one of --mise-bin / --remote-mise / --bootstrap-command (clap rejects combinations anyway)","Re-run mise bootstrap remote <host> to confirm the host entry validates"],"exampleFix":"# before (mise.toml)\n[bootstrap.remote.hosts.build]\nhost = \"build.internal\"\nremote_mise = \"mise\"\nbootstrap_command = \"curl https://mise.run | sh\"\n\n# after\n[bootstrap.remote.hosts.build]\nhost = \"build.internal\"\nbootstrap_command = \"curl https://mise.run | sh\"","handlingStrategy":"validation","validationCode":"# before running: confirm the host sets at most one strategy\npython3 - <<'EOF'\nimport tomllib, sys\ncfg = tomllib.load(open(\"mise.toml\", \"rb\"))\nfor name, host in cfg.get(\"bootstrap\", {}).get(\"remote\", {}).get(\"hosts\", {}).items():\n    keys = [k for k in (\"mise_bin\", \"remote_mise\", \"bootstrap_command\") if k in host]\n    if len(keys) > 1:\n        sys.exit(f\"host '{name}' sets multiple strategies: {keys}\")\nprint(\"ok\")\nEOF","typeGuard":null,"tryCatchPattern":"if let Err(e) = remote::hosts_from_config(&config, &excludes) {\n    if e.to_string().contains(\"must set at most one of mise_bin\") {\n        // fix the named host's config and re-run; do not retry unchanged\n    }\n}","preventionTips":["Keep one commented example strategy per host template so only one key is ever live","Rely on CLI flags for experiments — clap's conflicts_with_all rejects combinations instantly","Lint layered config files together, since later files merge keys into the same host entry"],"tags":["mise","config","validation","bootstrap","remote","mutually-exclusive"],"backgroundTag":"mutually-exclusive-config-options","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}