{"record":{"id":"ced3fb3512c2c9a7","repo":"jdx/mise","slug":"conflicting-bootstrap-service-declarations-for-na","errorCode":null,"errorMessage":"conflicting bootstrap service declarations for {name}\\n\\n  first:\\n    {}\\n\\n  second:\\n    {}","messagePattern":"conflicting bootstrap service declarations for (.+?)\\\\n\\\\n  first:\\\\n    (.+?)\\\\n\\\\n  second:\\\\n    (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/services.rs","lineNumber":150,"sourceCode":"    dependency_changed: bool,\n    notified: bool,\n    active: bool,\n}\n\n#[derive(Clone, Debug, Default, Serialize, Deserialize)]\nstruct ServicePlan {\n    actions: Vec<ServiceAction>,\n}\n\npub(crate) fn prepare_requests_from_config(config: &Config) -> Result<Vec<ServiceRequest>> {\n    let mut composed: IndexMap<String, (ServiceTomlConfig, ResourceOrigin)> = IndexMap::new();\n    for config_files in config.bootstrap_config_maps() {\n        for (name, declaration) in services_from_config_files(config_files) {\n            if let Some(existing) = composed.get(&name) {\n                if existing.0 == declaration.0 {\n                    continue;\n                }\n                bail!(\n                    \"conflicting bootstrap service declarations for {name}\\n\\n  first:\\n    {}\\n\\n  second:\\n    {}\",\n                    existing.1.conflict_description(),\n                    declaration.1.conflict_description(),\n                );\n            }\n            composed.insert(name, declaration);\n        }\n    }\n    composed\n        .into_iter()\n        .map(|(name, (config, origin))| {\n            ServiceRequest::from_toml_with_origin(name, config, Some(origin))\n        })\n        .collect()\n}\n\nfn services_from_config_files(\n    config_files: &ConfigMap,","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/services.rs#L132-L168","documentation":"Service declarations are collected from every bootstrap config layer (prepare_requests_from_config, src/system/services.rs:150). When two layers declare the same service name, identical declarations are merged silently — but if the TOML bodies differ, bootstrap bails, printing the origin (file/section) of both the first and second declaration so you can see exactly which files fight.","triggerScenarios":"The same service name declared in two config files (project mise.toml plus user/global config, or multiple included files) with any difference in state, enabled, masked, or on_change fields. The conflict_description() values in the message identify both origins.","commonSituations":"Overriding a machine-wide service setting per-project instead of fully re-declaring it identically; an old config copy left in a parent directory; teams share a base config and a developer tweaked one copy.","solutions":["Open both files named in the 'first:'/'second:' blocks of the message and diff the two service declarations","Make them byte-identical (then the duplicate is harmless and merged), or","Delete the declaration from the layer that should not own it — one owner per service","Re-run bootstrap; collection succeeds once names no longer collide with differing bodies"],"exampleFix":"# before: ./mise.toml has\n[bootstrap.services.nginx]\nstate = \"running\"\n# ~/.config/mise/config.toml has\n[bootstrap.services.nginx]\nstate = \"stopped\"   # -> bail with both origins\n# after: keep it only in ./mise.toml (delete the user-level block)","handlingStrategy":"validation","validationCode":"# diff service declarations across config layers before bootstrap runs\npython3 - <<'PY'\nimport tomllib, glob, os, collections\nseen = {}\nfiles = ['mise.toml'] + glob.glob(os.path.expanduser('~/.config/mise/*.toml'))\nfor f in files:\n    svc = tomllib.load(open(f,'rb')).get('bootstrap',{}).get('services',{})\n    if not isinstance(svc, dict): continue\n    for name, body in svc.items():\n        if name in seen and seen[name] != body:\n            print(f\"conflicting declarations for service {name}: {seen[name]['_file']} vs {f}\")\n        body = dict(body); body['_file'] = f\n        seen.setdefault(name, body)\nPY","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One owner per service: declare each service in a single config layer","When overriding is needed, re-declare the service identically in the more specific layer (identical bodies merge silently)","Remove stale parent-directory configs after moving a project"],"tags":["bootstrap","services","systemd","duplicate-config","mise"],"backgroundTag":"duplicate-config-declaration","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}