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
- Check that cmd.StderrPipe() is the first thing called on a freshly created exec.Cmd
- Ensure no other code assigns cmd.Stderr or calls cmd.Start/Wait before the pipe is created
- 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
- Always create StderrPipe before Start on a fresh exec.Cmd
- Never assign cmd.Stderr when using StderrPipe
- Cover runCommand with a unit test that exercises each install/uninstall path
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
- error starting %s: %v
- %s %v returned with error code %v due to: %v
- error piping traceroute's output: %w
- error retrieving output from command '%s': %w
- failed to get own start time: %w
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/6cf90197fe484cfa.
Report an issue: GitHub.