{"record":{"id":"26341cab03f06778","repo":"GitoxideLabs/gitoxide","slug":"the-undo-queue-cannot-record-itself","errorCode":null,"errorMessage":"the undo queue cannot record itself","messagePattern":"the undo queue cannot record itself","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/undo.rs","lineNumber":341,"sourceCode":"        title,\n        changes,\n        edits,\n        position: queue.position(cursor_index),\n    })\n}\n\nfn empty_position() -> Position {\n    Position {\n        title: START_TITLE.into(),\n        undo: 0,\n        redo: 0,\n    }\n}\n\nfn normalize_changes(changes: impl IntoIterator<Item = RefChange>) -> Result<Vec<RefChange>> {\n    let mut by_name = BTreeMap::<FullName, RefChange>::new();\n    for change in changes {\n        ensure!(\n            !is_queue_ref(change.name.as_bstr()),\n            \"the undo queue cannot record itself\"\n        );\n        match by_name.get_mut(&change.name) {\n            Some(existing) => {\n                ensure!(\n                    existing.after == change.before,\n                    \"successive changes to {} are not continuous\",\n                    change.name\n                );\n                existing.after = change.after;\n            }\n            None => {\n                by_name.insert(change.name.clone(), change);\n            }\n        }\n    }\n    Ok(by_name","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/undo.rs#L323-L359","documentation":"`normalize_changes` validates a batch of reference changes before they are recorded on the undo queue. Any change whose target name is one of the undo queue's own refs (tip, cursor, etc., matched by `is_queue_ref`) is rejected, because recording a change to the queue refs on the queue itself would create self-referential, un-undoable state.","triggerScenarios":"Calling `record`, `changes_from_edits`, or `apply_reversed_changes` with a `RefChange` whose `name` points at an undo-queue ref (e.g. the queue tip/cursor ref).","commonSituations":"Custom automation that enumerates all refs and builds ref updates blindly, capturing the queue's internal refs; misconfigured tools that treat queue refs as ordinary refs.","solutions":["Filter out queue refs from your change list before recording (use the same prefix test the library uses).","Never write directly to the undo queue's tip/cursor refs; use the library's public undo/redo APIs.","If you need to reset the queue, use its reset/discard API instead of crafting a RefChange."],"exampleFix":"// before: recording every ref change\nlet changes: Vec<RefChange> = all_ref_updates();\nrepo.undo_record(changes)?;\n// after: drop queue-internal refs first\nlet changes: Vec<RefChange> = all_ref_updates()\n    .into_iter()\n    .filter(|c| !crate::history::is_queue_ref(c.name.as_bstr()))\n    .collect();","handlingStrategy":"validation","validationCode":"// filter queue-internal refs out of any change batch before recording\nlet changes: Vec<RefChange> = changes\n    .into_iter()\n    .filter(|c| !crate::history::is_queue_ref(c.name.as_bstr()))\n    .collect();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never write to refs under the undo queue prefix directly.","When enumerating all refs for recording, always exclude the queue prefix.","Use the library's reset/discard APIs to manage queue refs."],"tags":["git","references","undo-queue","invalid-input"],"backgroundTag":"invalid-argument-value","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}