{"record":{"id":"626293191bb2ccb8","repo":"jj-vcs/jj","slug":"conflicting-factory-definitions-for-factory","errorCode":null,"errorMessage":"Conflicting factory definitions for '{}' factory","messagePattern":"Conflicting factory definitions for '(.+?)' factory","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"lib/src/repo.rs","lineNumber":428,"sourceCode":"    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendLoadError>>;\ntype OpStoreFactory = Box<\n    dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendLoadError>,\n>;\ntype OpHeadsStoreFactory =\n    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendLoadError>>;\ntype IndexStoreFactory =\n    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendLoadError>>;\ntype SubmoduleStoreFactory =\n    Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendLoadError>>;\n\npub fn merge_factories_map<F>(base: &mut HashMap<String, F>, ext: HashMap<String, F>) {\n    for (name, factory) in ext {\n        match base.entry(name) {\n            Entry::Vacant(v) => {\n                v.insert(factory);\n            }\n            Entry::Occupied(o) => {\n                panic!(\"Conflicting factory definitions for '{}' factory\", o.key())\n            }\n        }\n    }\n}\n\npub struct StoreFactories {\n    backend_factories: HashMap<String, BackendFactory>,\n    op_store_factories: HashMap<String, OpStoreFactory>,\n    op_heads_store_factories: HashMap<String, OpHeadsStoreFactory>,\n    index_store_factories: HashMap<String, IndexStoreFactory>,\n    submodule_store_factories: HashMap<String, SubmoduleStoreFactory>,\n}\n\n#[derive(Debug, Error)]\npub enum StoreLoadError {\n    #[error(\"Unsupported {store} backend type '{store_type}'\")]\n    UnsupportedType {\n        store: &'static str,","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/jj-vcs/jj/blob/6631dbd4a85d5eb801aab321f7a9649ae3dc86ba/lib/src/repo.rs#L410-L446","documentation":"This panic is thrown by merge_factories_map in lib/src/repo.rs when merging two StoreFactories maps (base and ext) and an extension tries to register a store factory under a name that already exists in the base map. Jujutsu (jj) uses per-backend store factories keyed by backend name, and each name must be unique so the correct backend can be resolved. A duplicate registration means two extensions (or an extension plus a built-in) claim the same backend name, which would make resolution ambiguous, so the library aborts immediately rather than picking one silently.","triggerScenarios":"Calling RepoEnv/AddBackend-style extension methods (add_working_copy_factories, or the higher-level merge/add-extension APIs that funnel into merge_factories_map) twice with the same backend name, e.g. registering the 'git' or 'local' store factory once via built-in defaults and again via a loaded extension. It also fires when merging two StoreFactories maps (merge) that both contain an entry for the same key.","commonSituations":"Re-initializing a RepoEnv/StoreFactories and re-adding default factories before adding custom ones; loading two jj extensions that both register a backend with the same name; a version change where defaults started including a factory (e.g. the git backend) that user code also registers manually; copy-pasting setup code so the same add_* call runs twice.","solutions":["Find and remove the duplicate registration: check whether the factory name is already present before adding (e.g. build StoreFactories::default() and inspect its keys instead of re-adding built-ins).","If you merged two factory maps with merge/merge_factories_map, ensure each map is populated from disjoint sources, or filter ext entries to only names not in base.","When writing an extension, give it a unique backend name rather than reusing 'git', 'local', etc.","Upgrade/downgrade to matching jj versions if defaults changed to include a factory your code also registers."],"exampleFix":"// before\nlet mut factories = StoreFactories::default();\nadd_git_factory(&mut factories); // 'git' already added by default\n\n// after\nlet mut factories = StoreFactories::default();\nif !factories.contains(&git_backend_name) {\n    add_git_factory(&mut factories);\n}","handlingStrategy":"validation","validationCode":"// Before merging/adding factories, check for collisions:\nfn merge_no_conflict(base: &mut StoreFactories, ext: StoreFactories) {\n    for name in ext.keys() {\n        assert!(!base.contains(name), \"factory '{}' already registered\", name);\n    }\n    base.merge(ext);\n}","typeGuard":null,"tryCatchPattern":"// Panics cannot be caught reliably in Rust; validate before merging instead (see validationCode). std::panic::catch_unwind is a last resort only.\nlet result = std::panic::catch_unwind(|| factories.merge(ext));\nif result.is_err() { /* rebuild factories and report config error */ }","preventionTips":["Initialize StoreFactories exactly once from defaults; never re-add built-in factories.","Namespace custom backend names to avoid collisions with 'git'/'local' or other extensions.","Add a startup assertion listing duplicate factory names before calling merge."],"tags":["rust","jujutsu","panic","configuration","duplicate-registration","factories"],"backgroundTag":"duplicate-plugin-registration","analyzedSha":"6631dbd4a85d5eb801aab321f7a9649ae3dc86ba","analyzedAt":"2026-08-28T15:03:31.143Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}