jesseduffield/lazygit · error
Expected exactly one fixup hash, found %d
Error message
Expected exactly one fixup hash, found %d
What it means
Companion check in moveFixupCommitDown: it counts todos with command Pick and the fixup commit's hash and requires exactly one. Zero means the fixup commit being moved is not in the todo file; two or more means duplicated pick entries for it. The strict count protects against moving the wrong entry onto the original commit.
Source
Thrown at pkg/utils/rebase_todo.go:263
}
func moveFixupCommitDown(todos []todo.Todo, originalHash string, fixupHash string, changeToFixup bool) ([]todo.Todo, error) {
isOriginal := func(t todo.Todo) bool {
return (t.Command == todo.Pick || t.Command == todo.Merge) && equalHash(t.Commit, originalHash)
}
isFixup := func(t todo.Todo) bool {
return t.Command == todo.Pick && equalHash(t.Commit, fixupHash)
}
originalHashCount := lo.CountBy(todos, isOriginal)
if originalHashCount != 1 {
return nil, fmt.Errorf("Expected exactly one original hash, found %d", originalHashCount)
}
fixupHashCount := lo.CountBy(todos, isFixup)
if fixupHashCount != 1 {
return nil, fmt.Errorf("Expected exactly one fixup hash, found %d", fixupHashCount)
}
_, fixupIndex, _ := lo.FindIndexOf(todos, isFixup)
_, originalIndex, _ := lo.FindIndexOf(todos, isOriginal)
newTodos := MoveElement(todos, fixupIndex, originalIndex+1)
if changeToFixup {
newTodos[originalIndex+1].Command = todo.Fixup
}
return newTodos, nil
}
func RemoveUpdateRefsForCopiedBranch(fileName string, commentChar byte) error {
todos, err := ReadRebaseTodoFile(fileName, commentChar)
if err != nil {
return errView on GitHub (pinned to c477a2959b)
Solutions
- Verify the fixup commit still exists as a single 'pick <hash>' line in .git-rebase-todo
- Remove duplicate pick lines for that hash or restart the rebase if the file is inconsistent
- If 'found 0', the fixup was already applied — refresh lazygit before attempting the squash
Defensive patterns
Strategy: validation
Validate before calling
# sanity check before fixup: fixup hash picked exactly once
grep -cE "^pick ${FULL_HASH}" .git/rebase-merge/git-rebase-merge/git-rebase-todo 2>/dev/null || grep -cE "^pick ${FULL_HASH}" .git/rebase-merge/git-rebase-todo # want exactly 1 Try / catch
Catch and branch on the count: 0 → fixup already applied, refresh and skip; 2+ → de-duplicate pick lines in the todo, then retry.
Prevention
- Keep the todo file free of duplicate picks before attempting squash operations
- Refresh lazygit's view after each rebase continuation
When it happens
Trigger: Squashing a fixup during interactive rebase when the fixup commit is missing from .git-rebase-todo (already applied/dropped) or appears as multiple pick lines.
Common situations: Same class as the original-hash error: externally modified todo files, stale lazygit models after a conflict resolution step, duplicated picks after rebase continuations.
Related errors
- Expected exactly one original hash, found %d
- Cannot squash commits in current branch: the HEAD commit is
- Todo %s not found in git-rebase-todo
- Expected exactly one merge commit with hash %s
- index outside of range of commits
AI-assisted analysis of jesseduffield/lazygit@c477a2959b (2026-08-15).
Data as JSON: /api/errors/5e46c64df5792d45.
Report an issue: GitHub.