{"record":{"id":"d2996c2d44315192","repo":"jj-vcs/jj","slug":"conflict-registering-revset-function-name","errorCode":null,"errorMessage":"Conflict registering revset function '{name}'","messagePattern":"Conflict registering revset function '(.+?)'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"lib/src/revset.rs","lineNumber":3520,"sourceCode":"    pub fn new() -> Self {\n        Self {\n            symbol_resolvers: vec![],\n            function_map: BUILTIN_FUNCTION_MAP.clone(),\n        }\n    }\n\n    pub fn symbol_resolvers(&self) -> &[Box<dyn SymbolResolverExtension>] {\n        &self.symbol_resolvers\n    }\n\n    pub fn add_symbol_resolver(&mut self, symbol_resolver: Box<dyn SymbolResolverExtension>) {\n        self.symbol_resolvers.push(symbol_resolver);\n    }\n\n    pub fn add_custom_function(&mut self, name: &'static str, func: RevsetFunction) {\n        match self.function_map.entry(name) {\n            hash_map::Entry::Occupied(_) => {\n                panic!(\"Conflict registering revset function '{name}'\")\n            }\n            hash_map::Entry::Vacant(v) => v.insert(func),\n        };\n    }\n}\n\n/// Information needed to parse revset expression.\n#[derive(Clone)]\npub struct RevsetParseContext<'a> {\n    pub aliases_map: &'a RevsetAliasesMap,\n    pub local_variables: HashMap<&'a str, ExpressionNode<'a>>,\n    pub user_email: &'a str,\n    pub date_pattern_context: DatePatternContext,\n    /// Special remote that should be ignored by default. (e.g. \"git\")\n    pub default_ignored_remote: Option<&'a RemoteName>,\n    pub fileset_aliases_map: &'a FilesetAliasesMap,\n    pub extensions: &'a RevsetExtensions,\n    pub workspace: Option<RevsetWorkspaceContext<'a>>,","sourceCodeStart":3502,"sourceCodeEnd":3538,"githubUrl":"https://github.com/jj-vcs/jj/blob/c09b0c337f0dbff496ad3d696684aa1128482c38/lib/src/revset.rs#L3502-L3538","documentation":"This panic occurs in RevsetAliasesMap/RevsetParseContext's add_custom_function (lib/src/revset.rs) when a custom revset function is registered under a name that is already present in the function map. Revset functions (like heads(x), parents(x)) are dispatched by name, so two registrations for the same name would make evaluation ambiguous. The library therefore panics on insert conflict instead of overwriting, surfacing configuration errors early.","triggerScenarios":"Calling add_custom_function(\"myfunc\", ...) twice with the same name, or registering a custom function whose name collides with a built-in (e.g. \"heads\", \"parents\", \"children\") or with a function added by another extension/config layer. Typically triggered while assembling revset parse contexts or loading revset function extensions.","commonSituations":"A jj extension that defines a helper like mine(x) being loaded twice (e.g. listed in config under two extension mechanisms); a version upgrade adding a new built-in revset function that collides with a user-defined custom function of the same name; copy-pasted initialization code registering the same custom function in two places.","solutions":["Rename your custom revset function to something namespaced/unique (e.g. myext::myfunc) so it cannot collide with built-ins or other extensions.","Audit init code and extension loading to ensure add_custom_function is called exactly once per name; cache the context rather than rebuilding it incrementally with duplicate inserts.","If the name is now a built-in in a newer jj version, remove your custom registration and use the built-in, or pin the older version.","Check the function_map before inserting and skip/warn instead of blindly calling add_custom_function."],"exampleFix":"// before\nctx.add_custom_function(\"mine\", mine_fn);\nctx.add_custom_function(\"mine\", mine_fn); // panics\n\n// after\nctx.add_custom_function(\"myext-mine\", mine_fn); // unique name, inserted once","handlingStrategy":"validation","validationCode":"// Guard before registering:\nif ctx.function_map_contains(name) {\n    eprintln!(\"revset function '{name}' already registered; skipping\");\n} else {\n    ctx.add_custom_function(name, func);\n}","typeGuard":null,"tryCatchPattern":"// Panics unwind the process in Rust; prefer pre-checking. Last resort:\nlet r = std::panic::catch_unwind(|| ctx.add_custom_function(name, func));\nif r.is_err() { /* log config error and continue */ }","preventionTips":["Prefix custom revset function names with an extension namespace.","Register custom functions in a single, idempotent init function.","After upgrading jj, diff the built-in function list against your custom names."],"tags":["rust","jujutsu","revset","panic","duplicate-registration","dsl"],"backgroundTag":"duplicate-function-registration","analyzedSha":"c09b0c337f0dbff496ad3d696684aa1128482c38","analyzedAt":"2026-08-28T15:03:31.143Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}