{"record":{"id":"d79b1a3ad131940c","repo":"jesseduffield/lazygit","slug":"index-outside-of-range-of-commits","errorCode":null,"errorMessage":"index outside of range of commits","messagePattern":"index outside of range of commits","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/commands/git_commands/patch.go","lineNumber":147,"sourceCode":"\t\t\treturn err\n\t\t}\n\n\t\t// amend the destination commit\n\t\tif err := self.commit.AmendHead(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tself.rebase.onSuccessfulContinue = func() error {\n\t\t\tself.PatchBuilder.Reset()\n\t\t\treturn nil\n\t\t}\n\n\t\t// continue\n\t\treturn self.rebase.ContinueRebase()\n\t}\n\n\tif len(commits)-1 < sourceCommitIdx {\n\t\treturn errors.New(\"index outside of range of commits\")\n\t}\n\n\t// we can make this GPG thing possible it just means we need to do this in two parts:\n\t// one where we handle the possibility of a credential request, and the other\n\t// where we continue the rebase\n\tif self.config.NeedsGpgSubprocessForCommit() {\n\t\treturn errors.New(self.Tr.DisabledForGPG)\n\t}\n\n\tbaseIndex := sourceCommitIdx + 1\n\n\tchanges := []daemon.ChangeTodoAction{\n\t\t{Hash: commits[sourceCommitIdx].Hash(), NewAction: todo.Edit},\n\t\t{Hash: commits[destinationCommitIdx].Hash(), NewAction: todo.Edit},\n\t}\n\tself.os.LogCommand(logTodoChanges(changes), false)\n\n\terr := self.rebase.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/jesseduffield/lazygit/blob/c477a2959b229fbf3284be0d4d2904ab61ec3c94/pkg/commands/git_commands/patch.go#L129-L165","documentation":"MovePatchIntoCommit (patch.go) validates that sourceCommitIdx is within the loaded commits slice before building todo changes; len(commits)-1 < sourceCommitIdx means the caller's index refers to a commit the model no longer contains. This is an internal consistency check between the UI's commit list and the command layer.","triggerScenarios":"The commits reloaded between selection and action (background refresh, rebase finished, branch switched) so the selected index is now out of range; calling the API with a stale index in scripts/tests.","commonSituations":"Race between a worker refresh and the user pressing the move-patch keybinding; very short commit lists (index 0 selected, list becomes empty).","solutions":["Re-check the index against the fresh model length at the call site before invoking (as the controllers do)","Trigger a refresh of the commits view and retry the action after the model updates","If reproducing programmatically, pass an index < len(commits) obtained from the same model snapshot"],"exampleFix":"// before\nerr := gitCommands.Patch.MovePatchIntoCommit(commits, selectedIdx, ...)\n\n// after\nif selectedIdx >= len(commits) {\n    return errors.New(\"selection out of range; refresh and retry\")\n}\nerr := gitCommands.Patch.MovePatchIntoCommit(commits, selectedIdx, ...)","handlingStrategy":"validation","validationCode":"if sourceCommitIdx < 0 || sourceCommitIdx >= len(commits) {\n    return fmt.Errorf(\"source index %d out of range for %d commits\", sourceCommitIdx, len(commits))\n}\nreturn gitCommands.Patch.MovePatchIntoCommit(commits, sourceCommitIdx, destIdx)","typeGuard":null,"tryCatchPattern":"On 'index outside of range of commits', refresh the commit log and recompute indexes once; if it persists, abort — it signals model/view divergence, not a transient git failure.","preventionTips":["Always compute indexes from the same []*models.Commit passed to the call","Refresh the commits view after any rebase/branch-switch before allowing selection-based actions","In tests, build the commits slice and indexes together"],"tags":["internal","index-out-of-range","model-staleness","rebase"],"backgroundTag":null,"analyzedSha":"c477a2959b229fbf3284be0d4d2904ab61ec3c94","analyzedAt":"2026-08-15T08:34:32.451Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}