cloudflare/cloudflared · error

error removing %s: %w

Error message

error removing %s: %w

What it means

After resolving the OpenRC template paths, uninstallOpenRC calls os.Remove on each service file. This error is returned when os.Remove fails for any reason other than the file not existing (ErrNotExist is deliberately tolerated so uninstall is idempotent). It signals a real filesystem obstacle such as missing permissions or the path being a non-empty directory.

Source

Thrown at cmd/cloudflared/linux_service.go:548

		}
	}
	return nil
}

func uninstallOpenRC(log *zerolog.Logger) error {
	if err := runCommand("rc-service", cloudflaredOpenRCService, "stop"); err != nil {
		log.Warn().Err(err).Msg("could not stop cloudflared OpenRC service, continuing uninstall")
	}
	if err := runCommand("rc-update", "del", cloudflaredOpenRCService, "default"); err != nil {
		log.Warn().Err(err).Msg("could not remove cloudflared from the default runlevel, continuing uninstall")
	}
	for _, template := range []ServiceTemplate{openrcTemplate, openrcConfTemplate} {
		path, err := template.ResolvePath()
		if err != nil {
			return fmt.Errorf("error resolving OpenRC template path: %w", err)
		}
		if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
			return fmt.Errorf("error removing %s: %w", path, err)
		}
	}
	return nil
}

func copyFile(src, dest string) error {
	srcFile, err := os.Open(src) //nolint:gosec // operator-provided service config path
	if err != nil {
		return err
	}
	defer func() { _ = srcFile.Close() }()

	destFile, err := os.Create(dest) //nolint:gosec // operator-provided service config path
	if err != nil {
		return err
	}
	ok := false
	defer func() {

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Re-run with root privileges: `sudo cloudflared service uninstall`
  2. Check and clear the immutable flag if set: `lsattr /etc/init.d/cloudflared; sudo chattr -i /etc/init.d/cloudflared`
  3. If the filesystem is read-only, remount rw (`mount -o remount,rw /etc` or boot normally) and retry
  4. Remove manually after fixing permissions: `sudo rm -f /etc/init.d/cloudflared /etc/conf.d/cloudflared`
  5. Check the wrapped (%w) error for the exact errno (EACCES vs EISDIR etc.) to pick the right fix

Example fix

// before: non-root uninstall
$ cloudflared service uninstall
// error: error removing /etc/init.d/cloudflared: remove /etc/init.d/cloudflared: permission denied
// after
$ sudo cloudflared service uninstall
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check writability before uninstall
func ensureRemovable(path string) error {
    if _, err := os.Stat(path); os.IsNotExist(err) {
        return nil // nothing to remove
    }
    if err := syscall.Access(filepath.Dir(path), unix.W_OK); err != nil {
        return fmt.Errorf("cannot remove %s: need write access to %s (run as root): %w", path, filepath.Dir(path), err)
    }
    return nil
}

Type guard

func fileRemovable(path string) bool {
    fi, err := os.Stat(path)
    if err != nil { return os.IsNotExist(err) } // absent = fine
    return !fi.IsDir() && fi.Mode().Perm()&0o200 != 0 || os.Geteuid() == 0
}

Try / catch

if err := uninstallLinuxService(c); err != nil {
    if strings.Contains(err.Error(), "error removing") {
        log.Warn("file removal blocked; check root privileges, read-only /etc, immutable flags (chattr -i), or SELinux denials")
    }
    return err
}

Prevention

When it happens

Trigger: During `cloudflared service uninstall` on OpenRC, when os.Remove(path) on /etc/init.d/cloudflared or /etc/conf.d/cloudflared fails with EACCES/EPERM (not root, read-only filesystem, immutable file) or EISDIR/ENOTEMPTY (path is a directory).

Common situations: Running uninstall without sudo; /etc mounted read-only (recovery mode, immutable container image); file or parent directory marked immutable (chattr +i); SELinux/AppArmor denial on removing init scripts; leftover directory where the file used to be.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/9d27e94c4d944038. Report an issue: GitHub.