jj-vcs/jj · critical
Conflicting factory definitions for '{}' factory
Error message
Conflicting factory definitions for '{}' factory What it means
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.
Source
Thrown at lib/src/repo.rs:428
Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn Backend>, BackendLoadError>>;
type OpStoreFactory = Box<
dyn Fn(&UserSettings, &Path, RootOperationData) -> Result<Box<dyn OpStore>, BackendLoadError>,
>;
type OpHeadsStoreFactory =
Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn OpHeadsStore>, BackendLoadError>>;
type IndexStoreFactory =
Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn IndexStore>, BackendLoadError>>;
type SubmoduleStoreFactory =
Box<dyn Fn(&UserSettings, &Path) -> Result<Box<dyn SubmoduleStore>, BackendLoadError>>;
pub fn merge_factories_map<F>(base: &mut HashMap<String, F>, ext: HashMap<String, F>) {
for (name, factory) in ext {
match base.entry(name) {
Entry::Vacant(v) => {
v.insert(factory);
}
Entry::Occupied(o) => {
panic!("Conflicting factory definitions for '{}' factory", o.key())
}
}
}
}
pub struct StoreFactories {
backend_factories: HashMap<String, BackendFactory>,
op_store_factories: HashMap<String, OpStoreFactory>,
op_heads_store_factories: HashMap<String, OpHeadsStoreFactory>,
index_store_factories: HashMap<String, IndexStoreFactory>,
submodule_store_factories: HashMap<String, SubmoduleStoreFactory>,
}
#[derive(Debug, Error)]
pub enum StoreLoadError {
#[error("Unsupported {store} backend type '{store_type}'")]
UnsupportedType {
store: &'static str,View on GitHub (pinned to 6631dbd4a8)
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.
Example fix
// before
let mut factories = StoreFactories::default();
add_git_factory(&mut factories); // 'git' already added by default
// after
let mut factories = StoreFactories::default();
if !factories.contains(&git_backend_name) {
add_git_factory(&mut factories);
} Defensive patterns
Strategy: validation
Validate before calling
// Before merging/adding factories, check for collisions:
fn merge_no_conflict(base: &mut StoreFactories, ext: StoreFactories) {
for name in ext.keys() {
assert!(!base.contains(name), "factory '{}' already registered", name);
}
base.merge(ext);
} Try / catch
// Panics cannot be caught reliably in Rust; validate before merging instead (see validationCode). std::panic::catch_unwind is a last resort only.
let result = std::panic::catch_unwind(|| factories.merge(ext));
if result.is_err() { /* rebuild factories and report config error */ } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Conflict registering revset function '{name}'
- cannot store git submodules
- SCM-aware Watchman clocks not supported
- ContentHash cannot be derived for unions.
AI-assisted analysis of jj-vcs/jj@6631dbd4a8 (2026-08-28).
Data as JSON: /api/errors/626293191bb2ccb8.
Report an issue: GitHub.