{"record":{"id":"6cf90197fe484cfa","repo":"cloudflare/cloudflared","slug":"error-getting-stderr-pipe-v","errorCode":null,"errorMessage":"error getting stderr pipe: %v","messagePattern":"error getting stderr pipe: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/cloudflared/service_template.go","lineNumber":96,"sourceCode":"\t\treturn fmt.Errorf(\"error deleting %s: %v\", resolvedPath, err)\n\t}\n\treturn nil\n}\n\nfunc serviceAlreadyExistsWarn(service string) string {\n\treturn fmt.Sprintf(\"cloudflared service is already installed at %s; if you are running a cloudflared tunnel, you \"+\n\t\t\"can point it to multiple origins, avoiding the need to run more than one cloudflared service in the \"+\n\t\t\"same machine; otherwise if you are really sure, you can do `cloudflared service uninstall` to clean \"+\n\t\t\"up the existing service and then try again this command\",\n\t\tservice,\n\t)\n}\n\nfunc runCommand(command string, args ...string) error {\n\tcmd := exec.Command(command, args...)\n\tstderr, err := cmd.StderrPipe()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting stderr pipe: %v\", err)\n\t}\n\terr = cmd.Start()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error starting %s: %v\", command, err)\n\t}\n\n\toutput, _ := io.ReadAll(stderr)\n\terr = cmd.Wait()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s %v returned with error code %v due to: %v\", command, args, err, string(output))\n\t}\n\treturn nil\n}\n","sourceCodeStart":78,"sourceCodeEnd":110,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/cmd/cloudflared/service_template.go#L78-L110","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nerr = cmd.Start()\nstderr, err := cmd.StderrPipe()\n// after\nstderr, err := cmd.StderrPipe()\nerr = cmd.Start()","handlingStrategy":"try-catch","validationCode":"if _, err := exec.LookPath(command); err != nil {\n\treturn fmt.Errorf(\"init tool %s not found: %w\", command, err)\n}","typeGuard":"func cmdNotStarted(c *exec.Cmd) bool { return c.Process == nil }","tryCatchPattern":"if err := runCommand(\"systemctl\", \"enable\", svc); err != nil {\n\tif strings.Contains(err.Error(), \"stderr pipe\") {\n\t\t// internal misuse of exec.Cmd; report bug, do not retry\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["go","exec","service-management","process"],"backgroundTag":"invalid-state-transition","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}