{"record":{"id":"04b42710c2ee16e8","repo":"anomalyco/sst","slug":"failed-to-create-stderr-pipe-v","errorCode":null,"errorMessage":"failed to create stderr pipe: %v","messagePattern":"failed to create stderr pipe: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/runtime/python/python.go","lineNumber":206,"sourceCode":"\t\t}\n\n\t\t// Join paths\n\t\tpythonPath := strings.Join(pythonPaths, string(os.PathListSeparator))\n\t\tenv = append(env, \"PYTHONPATH=\"+pythonPath)\n\n\t\tresourceEncPath := filepath.Join(input.Build.Out, \"resource.enc\")\n\t\tenv = append(env, \"SST_KEY_FILE=\"+resourceEncPath)\n\t}\n\n\tcmd.Env = env\n\tcmd.Dir = workingDir\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create stdout pipe: %v\", err)\n\t}\n\tstderr, err := cmd.StderrPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create stderr pipe: %v\", err)\n\t}\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to start worker process: %v\", err)\n\t}\n\n\treturn &worker{\n\t\tstdout,\n\t\tstderr,\n\t\tcmd,\n\t}, nil\n\n}\n\nfunc (r *PythonRuntime) ShouldRebuild(functionID string, file string) bool {\n\t// Skip paths inside build artifacts, caches, or virtual envs to avoid feedback loops\n\tnormalized := filepath.ToSlash(file)\n\tfor _, dir := range []string{\".sst\", \".venv\", \"venv\", \"__pycache__\", \".git\", \"node_modules\", \".pytest_cache\", \".mypy_cache\", \".tox\"} {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/pkg/runtime/python/python.go#L188-L224","documentation":"Raised in PythonRuntime.Run (pkg/runtime/python/python.go:206) when cmd.StderrPipe() fails after StdoutPipe succeeded. Same failure family as the stdout pipe: the command has already been started, or the OS cannot allocate another pipe (fd exhaustion). It wraps the underlying exec error so the dev server can surface why the Python worker could not be wired up.","triggerScenarios":"OS fd exhaustion between the two pipe calls, or code path reusing a cmd that was already Start()ed (StderrPipe after Start returns an error).","commonSituations":"Many concurrent Python functions in sst dev exhausting file descriptors; sandboxed CI runners with tiny fd limits; modified runtime code that starts the command before wiring pipes.","solutions":["Inspect the wrapped error; if it says 'after Start', always create both pipes before cmd.Start()","Raise ulimit -n and/or lower concurrency (SST_BUILD_CONCURRENCY_FUNCTION)","Restart the dev session to release leaked fds from prior workers"],"exampleFix":"// before\nstdout, _ := cmd.StdoutPipe()\ncmd.Start()\nstderr, err := cmd.StderrPipe() // error\n// after\nstdout, err := cmd.StdoutPipe()\nif err != nil { return nil, err }\nstderr, err := cmd.StderrPipe()\nif err != nil { return nil, err }\ncmd.Start()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"w, err := rt.Run(ctx, input)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to create stderr pipe\") {\n        // pipes must both be created before cmd.Start()\n        return recreateWorkerWithPipesFirst(ctx, input)\n    }\n    return err\n}","preventionTips":["Create stdout and stderr pipes before cmd.Start(), never after","Monitor open fds during long dev sessions; restart the session if they climb","Raise the fd limit in CI containers (ulimit -n 4096)","Ensure worker.Stop() is invoked on shutdown paths"],"tags":["process","pipe","file-descriptors","go"],"backgroundTag":"process-spawn-failed","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}