plandex-ai/plandex · error

error popping git stash: %v

Error message

error popping git stash: %v

What it means

When `git stash pop` fails but its output does NOT contain the conflict marker 'overwritten by merge', GitStashPop cannot classify the failure and returns this generic error carrying git's combined output. It signals an unexpected stash-pop failure outside the known conflict path.

Source

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

			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))
			}
			return nil
		} else {
			log.Println("No conflicts detected")

			return fmt.Errorf("error popping git stash: %v", string(res))
		}
	}

	return nil
}

func GitClearUncommittedChanges() error {
	gitMutex.Lock()
	defer gitMutex.Unlock()

	// Reset staged changes
	res, err := exec.Command("git", "reset", "--hard").CombinedOutput()
	if err != nil {
		return fmt.Errorf("error resetting staged changes | err: %v, output: %s", err, string(res))
	}

	// Clean untracked files
	res, err = exec.Command("git", "clean", "-d", "-f").CombinedOutput()

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read the output embedded in the error — it is the raw git stash pop stderr/stdout.
  2. If it says 'No stash entries found', either skip popping or guard the flow with GitStashCreate before pop.
  3. If untracked-file collision ('already exists'), delete/rename the offending untracked files and pop again.
  4. If output shows an overwrite conflict phrased differently, update PopStashConflictMsg/parseConflictFiles for your git version or resolve manually (`git checkout --ours` + `git stash drop`).
  5. Abort in-progress rebase/merge states before popping.

Example fix

// before: pop assumed a stash exists
lib.GitStashPop(false)
// after: ensure a stash exists first
if stashed, _ := lib.CheckUncommittedChanges(); stashed {
    lib.GitStashCreate("pre-update")
    lib.GitStashPop(false)
}
Defensive patterns

Strategy: validation

Validate before calling

// Go: only pop if a stash actually exists
out, err := exec.Command("git", "stash", "list").Output()
if err == nil && strings.TrimSpace(string(out)) != "" {
    lib.GitStashPop(false)
}

Type guard

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

Try / catch

if err := lib.GitStashPop(false); err != nil {
    if isStashPopError(err) {
        out := extractAfter(err.Error(), "output: ")
        if strings.Contains(out, "No stash entries found") {
            // expected when nothing was stashed — proceed
        } else if strings.Contains(out, "already exists") {
            // untracked-file collision: remove/rename file and retry
        } else {
            // unknown failure: log full git output for diagnosis
        }
    }
}

Prevention

When it happens

Trigger: Calling GitStashPop when there is no stash to pop ('No stash entries found'), when pop fails for merge reasons producing different wording than the 2.39.3 marker (git version drift), when the working tree has untracked files that would be clobbered ('already exists, no checkout'), or when the index is locked.

Common situations: GitStashCreate never ran or its stash was already consumed; newer/older git versions phrase overwrite warnings differently than PopStashConflictMsg; untracked file collisions on pop; running in a repo with an in-progress rebase/merge.

Related errors


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