plandex-ai/plandex · error

failed to commit changes: %s

Error message

failed to commit changes: %s

What it means

After the user confirms, commitApplied builds a commit message from the pending-changes summary and runs GitAddAndCommitPaths to commit only the updated files in the project root. This error wraps any git failure (add or commit) returned by that helper.

Source

Thrown at app/cli/lib/apply.go:615

		fmt.Println("✏️  Plandex can commit these updates with an automatically generated message.")
		fmt.Println()
		// fmt.Println("ℹ️  Only the files that Plandex is updating will be included the commit. Any other changes, staged or unstaged, will remain exactly as they are.")
		// fmt.Println()
		confirmed, err = term.ConfirmYesNo("Commit Plandex updates now?")
		if err != nil {
			return fmt.Errorf("failed to get confirmation user input: %s", err)
		}
	}

	if confirmed {
		// Commit the changes
		msg := currentPlanState.PendingChangesSummaryForApply(commitSummary)
		// log.Println("Committing changes with message:")
		// log.Println(msg)
		// spew.Dump(currentPlanState)
		err = GitAddAndCommitPaths(fs.ProjectRoot, msg, updatedFiles, true)
		if err != nil {
			return fmt.Errorf("failed to commit changes: %s", err.Error())
		}
	}

	return nil
}

func ApplyFiles(toApply map[string]string, toRemove map[string]bool, projectPaths *types.ProjectPaths) ([]string, *types.ApplyRollbackPlan, error) {
	var updatedFiles []string
	toRevert := map[string]types.ApplyReversion{}
	var toRemoveOnRollback []string

	var mu sync.Mutex
	totalOps := len(toApply) + len(toRemove)
	errCh := make(chan error, totalOps)

	for path, content := range toApply {
		if path == "_apply.sh" {
			errCh <- nil

View on GitHub (pinned to e2d772072e)

Solutions

  1. Run 'git add <file> && git commit' manually to see the underlying git error
  2. Set git identity: git config --global user.email/user.name
  3. Delete a stale .git/index.lock if no git process is running
  4. Ensure the project root is a git repository (git init if needed) and that git is installed and on PATH

Example fix

// before
$ plandex apply  # failed to commit changes: exit status 128
// after
$ git config user.email "you@example.com" && git config user.name "You"
$ plandex apply
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight git checks
if _, err := os.Stat(filepath.Join(fs.ProjectRoot, ".git")); err != nil {
  return errors.New("project root is not a git repository")
}
if err := exec.Command("git", "-C", fs.ProjectRoot, "diff-index", "--quiet", "HEAD").Run(); err != nil && !isDirtyExpected {
  // also verifies repo + git binary work
}
for _, f := range []string{"user.email", "user.name"} {
  if out, err := exec.Command("git", "config", f).Output(); err != nil || len(out) == 0 {
    return fmt.Errorf("git config %s is not set", f)
  }
}

Try / catch

if err := GitAddAndCommitPaths(fs.ProjectRoot, msg, updatedFiles, true); err != nil {
  if strings.Contains(err.Error(), "index.lock") {
    os.Remove(filepath.Join(fs.ProjectRoot, ".git", "index.lock")) // retry once
  }
  return fmt.Errorf("commit failed: %w", err)
}

Prevention

When it happens

Trigger: GitAddAndCommitPaths(fs.ProjectRoot, msg, updatedFiles, true) fails — git executable missing, directory is not a git repo, no user.name/user.email configured, another git process holds index.lock, or a path in updatedFiles is invalid.

Common situations: Fresh checkout without git init; cloned repo without git config user.email/user.name; stale index.lock from a crashed git process; file paths with unusual characters or outside the repo.

Related errors


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