plandex-ai/plandex · error

conflict popping git stash: %s

Error message

conflict popping git stash: %s

What it means

GitStashPop detects a merge conflict when applying the stash — git's output contains the marker 'overwritten by merge' (PopStashConflictMsg). When forceOverwrite is false, the function refuses to resolve conflicts itself and returns this error with git's raw output instead. The caller must decide whether to retry with forceOverwrite=true.

Source

Thrown at app/cli/lib/git.go:142

func GitStashPop(forceOverwrite bool) error {
	gitMutex.Lock()
	defer gitMutex.Unlock()

	res, err := exec.Command("git", "stash", "pop").CombinedOutput()

	// we should no longer have conflicts since we are forcing an update before
	// running the 'apply' command as well as resetting any files with uncommitted change
	// still leaving this though in case something goes wrong

	if err != nil {
		log.Println("Error popping git stash:", string(res))

		if strings.Contains(string(res), PopStashConflictMsg) {
			log.Println("Conflicts detected")

			if !forceOverwrite {
				return fmt.Errorf("conflict popping git stash: %s", string(res))
			}

			// Parse the output to find which files have conflicts
			conflictFiles := parseConflictFiles(string(res))

			log.Println("Conflicting files:", conflictFiles)

			for _, file := range conflictFiles {
				// Reset each conflicting file individually
				checkoutRes, err := exec.Command("git", "checkout", "--ours", file).CombinedOutput()
				if err != nil {
					return fmt.Errorf("error resetting file %s: %v", file, string(checkoutRes))
				}
			}
			dropRes, err := exec.Command("git", "stash", "drop").CombinedOutput()
			if err != nil {
				return fmt.Errorf("error dropping git stash: %v", string(dropRes))
			}

View on GitHub (pinned to e2d772072e)

Solutions

  1. If overwriting local changes is acceptable, call GitStashPop(true) to force-resolve with `git checkout --ours` per conflicted file.
  2. Inspect the git output in the error to see which files conflict.
  3. Manually commit or discard the interfering local changes, then pop again.
  4. Re-run the operation that expects a clean tree (e.g. GitClearUncommittedChanges) before popping.
  5. If the stash content is no longer needed, `git stash drop` instead of popping.

Example fix

// before
err := lib.GitStashPop(false)
// after: force-resolve conflicts by taking current tree
err := lib.GitStashPop(true)
Defensive patterns

Strategy: fallback

Validate before calling

// Go: minimize conflict window by restoring/updating the tree right before pop
if dirty, _ := lib.CheckUncommittedChanges(); dirty {
    lib.GitClearUncommittedChanges()
}

Type guard

func isStashConflict(err error) bool {
    return err != nil && strings.Contains(err.Error(), "conflict popping git stash")
}

Try / catch

if err := lib.GitStashPop(false); err != nil {
    if isStashConflict(err) {
        // escalate: caller decides whether local changes may be clobbered
        if allowOverwrite {
            err = lib.GitStashPop(true)
        }
    }
}

Prevention

When it happens

Trigger: Calling GitStashPop(false) after GitStashCreate when tracked files changed between stash and pop such that applying the stash would overwrite local modifications — i.e. git reports 'Your local changes to the following files would be overwritten by merge'.

Common situations: Files modified externally (editor, build tooling, another process) between stash and pop; applying a stash created before a rewind/update operation onto changed working tree; concurrent agents editing the same checkout.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/affd6914969b4f03. Report an issue: GitHub.