plandex-ai/plandex · error

failed to remove %s: %s

Error message

failed to remove %s: %s

What it means

After snapshotting content for rollback, ApplyFiles deletes the file with os.Remove. If removal fails for any reason other than the file already not existing, this error is emitted and the apply aborts.

Source

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

				exists = true
				mode = info.Mode()
			} else {
				if os.IsNotExist(err) {
					exists = false
				} else {
					errCh <- fmt.Errorf("failed to check if %s exists: %s", dstPath, err.Error())
					return
				}
			}
			if exists {
				content, err := os.ReadFile(dstPath)
				if err != nil {
					errCh <- fmt.Errorf("failed to read %s: %s", dstPath, err.Error())
					return
				}
				err = os.Remove(dstPath)
				if err != nil && !os.IsNotExist(err) {
					errCh <- fmt.Errorf("failed to remove %s: %s", dstPath, err.Error())
					return
				}
				mu.Lock()
				toRevert[dstPath] = types.ApplyReversion{Content: string(content), Mode: mode}
				mu.Unlock()
			}
			errCh <- nil
		}(path, remove)
	}

	for i := 0; i < totalOps; i++ {
		err := <-errCh
		if err != nil {
			return nil, nil, err
		}
	}

	return updatedFiles, &types.ApplyRollbackPlan{

View on GitHub (pinned to e2d772072e)

Solutions

  1. Ensure the containing directory is writable (chmod/chown the directory)
  2. If the target is a directory, remove it with rm -rf manually or fix the plan (os.Remove doesn't recurse)
  3. Remove immutable attributes (chattr -i) if set
  4. Re-run apply on a read-write filesystem from the correct project root

Example fix

// before
drwxr-xr-x 2 user user .    # cwd not writable -> cannot unlink files
// after
$ chmod u+w .   # or chown the directory
$ plandex apply
Defensive patterns

Strategy: validation

Validate before calling

// removal requires write permission on the PARENT directory
parent := filepath.Dir(dstPath)
if err := syscall.Access(parent, syscall.W_OK); err != nil {
  return fmt.Errorf("%s not writable; cannot unlink files in it", parent)
}
if info, err := os.Stat(dstPath); err == nil && info.IsDir() {
  return fmt.Errorf("%s is a directory; os.Remove cannot recurse", dstPath)
}

Type guard

func canUnlink(path string) bool {
  return syscall.Access(filepath.Dir(path), syscall.W_OK) == nil
}

Try / catch

if err := os.Remove(dstPath); err != nil && !os.IsNotExist(err) {
  if errors.Is(err, fs.ErrPermission) { /* make parent writable */ }
  return fmt.Errorf("remove %s: %w", dstPath, err)
}

Prevention

When it happens

Trigger: os.Remove(dstPath) fails and the error is not os.IsNotExist — write permission missing on the containing directory, file is a non-empty directory, file is immutable, or the filesystem is read-only.

Common situations: Directory not writable by the current user (removal requires directory write permission, not file permission); plan tries to remove a directory path; immutable/locked files on mounted volumes.

Related errors


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