{"record":{"id":"1165de7cac1e3b79","repo":"gitbutlerapp/gitbutler","slug":"an-edge-with-desired-order-desired-order-already-exists","errorCode":null,"errorMessage":"An edge with desired order {desired_order} already exists","messagePattern":"An edge with desired order (.+?) already exists","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-rebase/src/graph_rebase/mutate.rs","lineNumber":843,"sourceCode":"    pub fn add_edge(\n        &mut self,\n        child: impl ToSelector,\n        parent: impl ToSelector,\n        desired_order: usize,\n    ) -> Result<()> {\n        let child = self.history.normalize_selector(child.to_selector(self)?)?;\n        let parent = self.history.normalize_selector(parent.to_selector(self)?)?;\n\n        if has_path_connecting(&self.graph, parent.id, child.id, None) {\n            bail!(\"BUG: Add edge introduces a cycle\");\n        }\n\n        if self\n            .graph\n            .edges_directed(child.id, Direction::Outgoing)\n            .any(|edge| edge.weight().order == desired_order)\n        {\n            bail!(\"An edge with desired order {desired_order} already exists\");\n        }\n\n        self.graph.add_edge(\n            child.id,\n            parent.id,\n            Edge {\n                order: desired_order,\n            },\n        );\n\n        Ok(())\n    }\n\n    /// Removes all edges between a child and parent, returning the orders of the removed edges.\n    pub fn remove_edges(\n        &mut self,\n        child: impl ToSelector,\n        parent: impl ToSelector,","sourceCodeStart":825,"sourceCodeEnd":861,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-rebase/src/graph_rebase/mutate.rs#L825-L861","documentation":"add_edge inserts a child->parent edge with an explicit sibling order among a child's parents. The graph already has an outgoing edge from the child with the requested order, so adding another would create an ambiguous ordering; the library refuses rather than producing a corrupted parent sequence.","triggerScenarios":"Calling add_edge (public) with a desired_order that collides with an existing outgoing edge of child.id — e.g. adding two parents both at order 0, or re-adding an edge without first shifting orders of existing edges.","commonSituations":"Building merge-commit parent lists where orders weren't renumbered after an insertion/removal; retrying a failed graph mutation that partially applied; programmatic squashes that reattach parents with default order 0.","solutions":["Renumber existing outgoing edges of the child so the desired order is free before adding","Use an order greater than the current max outgoing edge order","Check existing edges with graph.edges_directed(child.id, Outgoing) to pick a free order"],"exampleFix":"// before\neditor.add_edge(child, new_parent, 0)?; // order 0 already taken\n// after\nlet next = editor.graph.edges_directed(child.id, Direction::Outgoing).count() as u64;\neditor.add_edge(child, new_parent, next)?;","handlingStrategy":"validation","validationCode":"let taken: HashSet<u64> = editor.graph.edges_directed(child.id, Direction::Outgoing)\n    .map(|e| e.weight().order).collect();\nif taken.contains(&desired_order) { return Err(anyhow!(\"order {} taken\", desired_order)); }","typeGuard":"fn order_free(editor: &Editor<impl RefMetadata>, child: Selector, order: u64) -> bool {\n    !editor.graph.edges_directed(child.id, Direction::Outgoing)\n        .any(|e| e.weight().order == order)\n}","tryCatchPattern":null,"preventionTips":["Renumber sibling edges after insertions/removals","Track the next free order per child node","Never assume a fixed default order when adding multiple parents"],"tags":["rebase","graph","edge-order","conflict"],"backgroundTag":"invalid-argument-value","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}