cloudflare/cloudflared · error

error getting stderr pipe: %v

Error message

error getting stderr pipe: %v

What it means

runCommand wraps exec.Command's StderrPipe() failure. StderrPipe fails only when the exec.Cmd has already been started or waited on, or its Stdout/Stderr were already set as pipes — meaning the command object was misconfigured before launch. Since runCommand always constructs a fresh cmd, this indicates a logic bug in the service-template code path rather than a user problem.

Source

Thrown at cmd/cloudflared/service_template.go:96

		return fmt.Errorf("error deleting %s: %v", resolvedPath, err)
	}
	return nil
}

func serviceAlreadyExistsWarn(service string) string {
	return fmt.Sprintf("cloudflared service is already installed at %s; if you are running a cloudflared tunnel, you "+
		"can point it to multiple origins, avoiding the need to run more than one cloudflared service in the "+
		"same machine; otherwise if you are really sure, you can do `cloudflared service uninstall` to clean "+
		"up the existing service and then try again this command",
		service,
	)
}

func runCommand(command string, args ...string) error {
	cmd := exec.Command(command, args...)
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return fmt.Errorf("error getting stderr pipe: %v", err)
	}
	err = cmd.Start()
	if err != nil {
		return fmt.Errorf("error starting %s: %v", command, err)
	}

	output, _ := io.ReadAll(stderr)
	err = cmd.Wait()
	if err != nil {
		return fmt.Errorf("%s %v returned with error code %v due to: %v", command, args, err, string(output))
	}
	return nil
}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Check that cmd.StderrPipe() is the first thing called on a freshly created exec.Cmd
  2. Ensure no other code assigns cmd.Stderr or calls cmd.Start/Wait before the pipe is created
  3. Report a bug if this appears in an unmodified cloudflared build

Example fix

// before
err = cmd.Start()
stderr, err := cmd.StderrPipe()
// after
stderr, err := cmd.StderrPipe()
err = cmd.Start()
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := exec.LookPath(command); err != nil {
	return fmt.Errorf("init tool %s not found: %w", command, err)
}

Type guard

func cmdNotStarted(c *exec.Cmd) bool { return c.Process == nil }

Try / catch

if err := runCommand("systemctl", "enable", svc); err != nil {
	if strings.Contains(err.Error(), "stderr pipe") {
		// internal misuse of exec.Cmd; report bug, do not retry
	}
	return err
}

Prevention

When it happens

Trigger: Calling cmd.StderrPipe() after cmd.Start() or cmd.Wait() was already called on the same cmd, or after assigning cmd.Stderr. In this codebase it would only fire if runCommand's internals were reordered or the func were changed to reuse a cmd.

Common situations: Practically never seen by end users; it can appear when contributors refactor installSystemd/installSysv/installOpenRC/uninstallSystemd/uninstallSysv/uninstallOpenRC and accidentally start or reconfigure the command before requesting the pipe.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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