{"record":{"id":"6b026297a7ec092b","repo":"wavetermdev/waveterm","slug":"failed-to-create-stdout-pipe-w-6b0262","errorCode":null,"errorMessage":"failed to create stdout pipe: %w","messagePattern":"failed to create stdout pipe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/buildercontroller/buildercontroller.go","lineNumber":340,"sourceCode":"\t}\n\tfor k, v := range secretEnv {\n\t\tbuilderEnv[k] = v\n\t}\n\n\tcmd := exec.Command(appBinPath)\n\tcmd.Env = append(os.Environ(), \"TSUNAMI_CLOSEONSTDIN=1\")\n\n\tif wavebase.IsDevMode() {\n\t\tcmd.Env = append(cmd.Env, \"TSUNAMI_CORS=\"+tsunamiutil.DevModeCorsOrigins)\n\t}\n\n\tfor key, value := range builderEnv {\n\t\tcmd.Env = append(cmd.Env, key+\"=\"+value)\n\t}\n\n\tstdoutPipe, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create stdout pipe: %w\", err)\n\t}\n\n\tstderrPipe, err := cmd.StderrPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create stderr pipe: %w\", err)\n\t}\n\n\tstdinPipe, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create stdin pipe: %w\", err)\n\t}\n\n\tportChan := make(chan int, 1)\n\tportFound := false\n\n\tbc.outputBuffer.SetLineCallback(func(line string) {\n\t\tif !portFound {\n\t\t\tif port := build.ParseTsunamiPort(line); port > 0 {","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/buildercontroller/buildercontroller.go#L322-L358","documentation":"runBuilderApp launches a builder subprocess via os/exec and wires up its I/O. This error wraps the failure from cmd.StdoutPipe(), which Go's exec package returns when the process has already been started or the OS cannot allocate a pipe. It is thrown before the builder process starts, so no child process exists yet.","triggerScenarios":"cmd.StdoutPipe() returns non-nil err — practically only when cmd.Start() was already called on the same Cmd, or on rare OS pipe-allocation failure (fd exhaustion).","commonSituations":"Code accidentally starting the command twice; extreme fd exhaustion (ulimit -n hit) after leaking pipes/files in a long-running server; reusing a BuilderController Cmd struct incorrectly.","solutions":["Ensure cmd.Start() is called only once and only after all pipes are created","Check the wrapped error (%w) — if 'too many open files', fix fd leaks or raise ulimit","Re-run the build; transient fd exhaustion clears once other handles are released"],"exampleFix":"// before\ncmd.Start()\nstdoutPipe, err := cmd.StdoutPipe() // fails: process already started\n// after\nstdoutPipe, err := cmd.StdoutPipe()\nif err != nil { return nil, fmt.Errorf(\"failed to create stdout pipe: %w\", err) }\nerr = cmd.Start()","handlingStrategy":"try-catch","validationCode":"if cmd.Process != nil { panic(\"cmd already started\") } // create pipes only on a fresh exec.Cmd","typeGuard":null,"tryCatchPattern":"process, err := bc.runBuilderApp(ctx, ...)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to create stdout pipe\") {\n        // fd exhaustion or double-start; inspect wrapped cause with errors.Unwrap\n    }\n    return err\n}","preventionTips":["Always create StdoutPipe/StderrPipe/StdinPipe immediately before Start, never after","Never reuse an exec.Cmd that has already been started","Monitor fd usage in long-running servers; close pipes and files promptly"],"tags":["go","os-exec","subprocess","pipe"],"backgroundTag":"os-exec-pipe-creation-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}