cloudflare/cloudflared · error

error resolving OpenRC template path: %w

Error message

error resolving OpenRC template path: %w

What it means

uninstallOpenRC calls ServiceTemplate.ResolvePath() (homedir.Expand) on both the OpenRC init script template and its conf template before deleting them, and aborts with this error if path resolution fails. Unlike the rc-update removal above it, a resolution failure is treated as fatal because the uninstall cannot know which file to remove.

Source

Thrown at cmd/cloudflared/linux_service.go:545

	for _, i := range [...]string{"0", "1", "6"} {
		if err := os.Remove("/etc/rc" + i + ".d/K02et"); err != nil {
			continue
		}
	}
	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

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Run with a valid HOME: `sudo HOME=/root cloudflared service uninstall`
  2. Verify the environment: `echo $HOME` must point to an existing directory and the user must resolve in passwd
  3. Workaround: delete the files manually — `sudo rm /etc/init.d/cloudflared /etc/conf.d/cloudflared` then `sudo rc-update del cloudflared default`
  4. Inspect the wrapped error to see which template path failed and why

Example fix

// before
$ cloudflared service uninstall  # HOME unset in container
// error: error resolving OpenRC template path: error resolving path /etc/init.d/cloudflared: ...
// after
$ sudo HOME=/root cloudflared service uninstall
Defensive patterns

Strategy: validation

Validate before calling

// guard before OpenRC uninstall
func ensureOpenRCUninstallEnv() error {
    if os.Getenv("HOME") == "" {
        return errors.New("HOME unset; run with: sudo HOME=/root cloudflared service uninstall")
    }
    if _, err := exec.LookPath("rc-update"); err != nil {
        return errors.New("rc-update not found: not an OpenRC system")
    }
    return nil
}

Type guard

func openrcPathsResolvable() bool {
    for _, p := range []string{"/etc/init.d/cloudflared", "/etc/conf.d/cloudflared"} {
        if _, err := os.Stat(p); err != nil && !os.IsNotExist(err) {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: During `cloudflared service uninstall` on an OpenRC system, when homedir.Expand fails on the openrcTemplate or openrcConfTemplate path — i.e. the path contains an unresolvable '~' because HOME is unset/invalid or the invoking user has no passwd entry.

Common situations: Uninstalling from an environment with no HOME variable (cron, container, CI job); running under a UID missing from /etc/passwd; restricted sandbox where user lookups fail (CGO-disabled builds with unusual NSS).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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