wavetermdev/waveterm · error
failed to create stdout pipe: %w
Error message
failed to create stdout pipe: %w
What it means
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.
Source
Thrown at pkg/buildercontroller/buildercontroller.go:340
}
for k, v := range secretEnv {
builderEnv[k] = v
}
cmd := exec.Command(appBinPath)
cmd.Env = append(os.Environ(), "TSUNAMI_CLOSEONSTDIN=1")
if wavebase.IsDevMode() {
cmd.Env = append(cmd.Env, "TSUNAMI_CORS="+tsunamiutil.DevModeCorsOrigins)
}
for key, value := range builderEnv {
cmd.Env = append(cmd.Env, key+"="+value)
}
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("failed to create stdout pipe: %w", err)
}
stderrPipe, err := cmd.StderrPipe()
if err != nil {
return nil, fmt.Errorf("failed to create stderr pipe: %w", err)
}
stdinPipe, err := cmd.StdinPipe()
if err != nil {
return nil, fmt.Errorf("failed to create stdin pipe: %w", err)
}
portChan := make(chan int, 1)
portFound := false
bc.outputBuffer.SetLineCallback(func(line string) {
if !portFound {
if port := build.ParseTsunamiPort(line); port > 0 {View on GitHub (pinned to a4447c1563)
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
Example fix
// before
cmd.Start()
stdoutPipe, err := cmd.StdoutPipe() // fails: process already started
// after
stdoutPipe, err := cmd.StdoutPipe()
if err != nil { return nil, fmt.Errorf("failed to create stdout pipe: %w", err) }
err = cmd.Start() Defensive patterns
Strategy: try-catch
Validate before calling
if cmd.Process != nil { panic("cmd already started") } // create pipes only on a fresh exec.Cmd Try / catch
process, err := bc.runBuilderApp(ctx, ...)
if err != nil {
if strings.Contains(err.Error(), "failed to create stdout pipe") {
// fd exhaustion or double-start; inspect wrapped cause with errors.Unwrap
}
return err
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- failed to create stderr pipe: %w
- failed to create stdin pipe: %w
- failed to start process: %w
- process died before emitting port
- timeout waiting for port
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/6b026297a7ec092b.
Report an issue: GitHub.