plandex-ai/plandex · error
panic in gitRemoveIndexLockFileIfExists: %v %s
Error message
panic in gitRemoveIndexLockFileIfExists: %v %s
What it means
gitRemoveIndexLockFileIfExists spawns one goroutine per lock-file path, each with a recover() that converts any panic into this error (including the stack trace via debug.Stack()) sent to errCh. You see this error when one of the per-path goroutines panicked — almost always a nil-pointer/nil-map or similar bug in the removal path rather than a git or filesystem condition. The recover ensures the panic doesn't crash the process; runtime.Goexit() prevents a double-send to the channel.
Source
Thrown at app/server/db/git.go:620
}
func gitRemoveIndexLockFileIfExists(repoDir string) error {
log.Printf("[Git] gitRemoveIndexLockFileIfExists - repoDir: %s", repoDir)
paths := []string{
filepath.Join(repoDir, ".git", "index.lock"),
filepath.Join(repoDir, ".git", "refs", "heads", "HEAD.lock"),
filepath.Join(repoDir, ".git", "HEAD.lock"),
}
errCh := make(chan error, len(paths))
for _, path := range paths {
go func(path string) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in gitRemoveIndexLockFileIfExists: %v\n%s", r, debug.Stack())
errCh <- fmt.Errorf("panic in gitRemoveIndexLockFileIfExists: %v\n%s", r, debug.Stack())
runtime.Goexit() // don't allow outer function to continue and double-send to channel
}
}()
if err := removeLockFile(path); err != nil {
errCh <- err
return
}
errCh <- nil
}(path)
}
errs := []error{}
for i := 0; i < len(paths); i++ {
err := <-errCh
if err != nil {
errs = append(errs, err)
}
}View on GitHub (pinned to e2d772072e)
Solutions
- Read the stack trace appended to the error message — it pinpoints the exact panicking function and line.
- Fix the underlying nil pointer / bad type assertion at that line; this error is a symptom, not the root cause.
- If caused by a recent change, diff removeLockFile / gitRemoveIndexLockFileIfExists against the last working version.
- Confirm inputs are valid (non-empty repoDir that contains a .git directory) before invoking write operations.
Example fix
// before: unchecked assumption inside goroutine work
idx := someMap["key"].(string) // panics if missing
// after
idx, ok := someMap["key"].(string)
if !ok {
errCh <- fmt.Errorf("missing key in map")
return
} Defensive patterns
Strategy: try-catch
Validate before calling
if repoDir == "" {
return errors.New("repoDir must be non-empty")
}
if fi, err := os.Stat(filepath.Join(repoDir, ".git")); err != nil || !fi.IsDir() {
return fmt.Errorf("%s is not a git repository", repoDir)
} Type guard
func isPanicErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "panic in gitRemoveIndexLockFileIfExists")
} Try / catch
if err := gitCommit(repoDir, msg); err != nil {
if isPanicErr(err) {
log.Printf("recovered panic in lock cleanup, stack: %s", err)
return fmt.Errorf("internal bug in lock cleanup: %w", err)
}
return err
} Prevention
- Run go vet and race detector (go test -race) on code touching removeLockFile.
- Never modify the goroutine/channel logic without re-reviewing the deferred recover and Goexit pattern.
- Keep stack traces from these errors in logs — they are the only pointer to the true panic site.
- Cover gitRemoveIndexLockFileIfExists with unit tests before refactoring it.
When it happens
Trigger: Any panic inside the deferred-recover-protected goroutine in gitRemoveIndexLockFileIfExists while it processes .git/index.lock, .git/refs/heads/HEAD.lock, or .git/HEAD.lock — e.g. a nil error value being dereferenced or channel misuse reaching the goroutine.
Common situations: A code change introduced a nil dereference or unchecked type assertion in removeLockFile/its helpers; running under unusual runtime conditions (stack exhaustion) surfaces latent panics; modified forked code passes invalid arguments (e.g. empty repoDir producing weird paths, though normally not a panic).
Related errors
- panic in UpdateContexts: %v\n%s
- panic in GetPlanConvo: %v\n%s
- panic in DeleteDraftPlans: %v %s
- panic in GetFullCurrentPlanStateParams: %v\n%s
- panic in GetCurrentPlanState: %v\n%s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/495d41a4f1459fb2.
Report an issue: GitHub.