fatal error: all goroutines are asleep - deadlock!
Describe the bug
fatal error: all goroutines are asleep - deadlock!
To Reproduce Steps to reproduce the behavior (macOS Sonoma latest version, latest go installation from its website):
-
go install github.com/jesseduffield/lazygit@latest
Expected behavior Open LazyGit interface
Version info:
lazygit version
commit=, build date=, build source=unknown, version=unversioned, os=darwin, arch=amd64, git version=2.46.0
git version
git version 2.46.0
Additional context Running on macOS Sonoma 14.6 Have added ~/go/bin to $PATH as instructed.
POTENTIAL DEADLOCK: Recursive locking: when running lazygit --debug
Your reproduction recipe shows pretty much basic, normal lazygit usage as far as I can tell. There must be something more that is specific to your setup, otherwise lazygit would be unusable for everybody.
Are you starting lazygit from within a git repo? Does the problem happen for all your repos, or only certain ones? Have you checked if an official build (e.g. the homebrew version) has the same problem?
Are you starting lazygit from within a git repo?
Yes, a good example is the mantinedev/mantine repo I've cloned. It also happens to one of my own repos.
Have you checked if an official build (e.g. the homebrew version) has the same problem?
Yes, have tried using the Homebrew version and I still get the same error.
On a tangent, with the steps above, I don't get this error on an Apple Silicon. Though when running lazygit --debug, it just hangs without any output.
I was looking into #3903 and ran into this issue with a binary compiled from a local checkout. Here's the trace from two different attempts:
Attempt 1
POTENTIAL DEADLOCK: Inconsistent locking. saw this ordering in one goroutine:
happened before
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:273 helpers.(*RefreshHelper).refreshReflogAndBranches { self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, loadBehindCounts) }
pkg/gui/controllers/helpers/refresh_helper.go:129 helpers.(*RefreshHelper).Refresh.func2.2 { if self.c.AppState.LocalBranchSortOrder == "recency" { }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
happened after
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 { }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError { }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }
in another goroutine: happened before
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 { }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError { }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }
happened after
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:258 helpers.(*RefreshHelper).refreshReflogCommitsConsideringStartup.func1 { self.refreshBranches(false, true, true) }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
Other goroutines holding locks:
goroutine 2 lock 0xc00040bb38
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:512 helpers.(*RefreshHelper).refreshFilesAndSubmodules { self.c.Mutexes().RefreshingFilesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:154 helpers.(*RefreshHelper).Refresh.func2.8 { _ = self.refreshFilesAndSubmodules() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
goroutine 2 lock 0xc0003d3128
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 { }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError { }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }
goroutine 2 lock 0xc00040bb50
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:322 helpers.(*RefreshHelper).refreshCommitsWithLimit { self.c.Mutexes().LocalCommitsMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:277 helpers.(*RefreshHelper).refreshCommitsAndCommitFiles { _ = self.refreshCommitsWithLimit() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
Attempt 2
POTENTIAL DEADLOCK: Recursive locking:
current goroutine 2 lock 0xc00040db40
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:258 helpers.(*RefreshHelper).refreshReflogCommitsConsideringStartup.func1 { self.refreshBranches(false, true, true) }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
Previous place where the lock was grabbed (same goroutine)
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:273 helpers.(*RefreshHelper).refreshReflogAndBranches { self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, loadBehindCounts) }
pkg/gui/controllers/helpers/refresh_helper.go:129 helpers.(*RefreshHelper).Refresh.func2.2 { if self.c.AppState.LocalBranchSortOrder == "recency" { }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
Other goroutines holding locks:
goroutine 2 lock 0xc00040db38
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:512 helpers.(*RefreshHelper).refreshFilesAndSubmodules { self.c.Mutexes().RefreshingFilesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:154 helpers.(*RefreshHelper).Refresh.func2.8 { _ = self.refreshFilesAndSubmodules() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
goroutine 2 lock 0xc00040db50
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:322 helpers.(*RefreshHelper).refreshCommitsWithLimit { self.c.Mutexes().LocalCommitsMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:277 helpers.(*RefreshHelper).refreshCommitsAndCommitFiles { _ = self.refreshCommitsWithLimit() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux { }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }
goroutine 2 lock 0xc00036a568
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 { }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError { }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }
For what it's worth, this was only when running a locally compiled version: commit=a04ad24a60584135e94a7eb2ff32c956e1e5a83d, build date=2024-09-18T11:26:47Z, build source=unknown, version=a04ad24a, os=linux, arch=amd64, git version=2.34.1
The downloaded version runs fine: commit=2d0c7cb0fc85e3c262837eb9691813fa97e00e80, build date=2024-09-07T02:26:26Z, build source=binaryRelease, version=0.44.0, os=linux, arch=amd64, git version=2.34.1
I've just seen this after a clean clone of the lazygit repo, setting up the devcontainer, then running go run main.go -debug from within the devcontainer. It also doesn't work in the codespace recommended in CONTRIBUTING.md.
However, it doesn't happen without -debug.
I also can't run any of the integration tests, which is making contributing something somewhat challenging...
FWIW, deadlock.Opts.DisableLockOrderDetection = true at the top of NewGui disables that error, so at least you can run the tests. I had a flick through the code it was unhappy about, and couldn't spot anything wrong.
https://github.com/jesseduffield/lazygit/issues/4002
Potentially connected to https://github.com/sasha-s/go-deadlock/issues/38