{"record":{"id":"6050b9ba6111023d","repo":"dotnet/runtime","slug":"dup2-stdout-failed-s-n","errorCode":null,"errorMessage":"dup2(stdout) failed: %s\\n","messagePattern":"dup2\\(stdout\\) failed: (.+?)\\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/coreclr/tools/superpmi/superpmi/parallelsuperpmi.cpp","lineNumber":215,"sourceCode":"        return false;\n    }\n\n    *pid = fork();\n    if (*pid == -1)\n    {\n        LogError(\"fork() failed: %s\", strerror(errno));\n        for (int i = 0; i < argc; i++)\n            delete[] argv[i];\n        delete[] argv;\n        return false;\n    }\n\n    if (*pid == 0)\n    {\n        // Child process: redirect stdout and stderr then exec.\n        if (dup2(hStdOutput, STDOUT_FILENO) == -1)\n        {\n            fprintf(stderr, \"dup2(stdout) failed: %s\\n\", strerror(errno));\n            _exit(1);\n        }\n        if (dup2(hStdError, STDERR_FILENO) == -1)\n        {\n            fprintf(stderr, \"dup2(stderr) failed: %s\\n\", strerror(errno));\n            _exit(1);\n        }\n\n        // Close the original fds now that they are duplicated to STDOUT/STDERR.\n        // The O_CLOEXEC on the parent's open() handles the sibling worker fds.\n        if (hStdOutput != STDOUT_FILENO)\n            close(hStdOutput);\n        if (hStdError != STDERR_FILENO)\n            close(hStdError);\n\n        execv(argv[0], argv);\n        // If execv returns, it failed.\n        fprintf(stderr, \"execv(%s) failed: %s\\n\", argv[0], strerror(errno));","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/coreclr/tools/superpmi/superpmi/parallelsuperpmi.cpp#L197-L233","documentation":"In ParallelSuperPMI's StartProcess the forked child redirects its stdout to a captured pipe handle with dup2(hStdOutput,STDOUT_FILENO). If dup2 returns -1 the child prints this message with strerror(errno) and _exit(1); the worker never execs and the parent sees a dead child. This is a child-side fatal during parallel worker launch.","triggerScenarios":"hStdOutput is not a valid open descriptor (already closed or never opened), the process has exhausted file descriptors (EMFILE), or the target descriptor is invalid. Occurs only inside the forked worker before execv in parallelsuperpmi.","commonSituations":"Launching hundreds of parallel workers and hitting the per-process fd limit; a bug/race where the output pipe was closed before dup2; running ParallelSuperPMI with an fd ulimit too low for the worker count.","solutions":["Raise the file-descriptor limit before running parallelsuperpmi (ulimit -n 1048576) so worker pipes fit.","Reduce the worker count (--workers/-j) so each child needs fewer fds.","Check the pipe-open code path that produced hStdOutput for an early close or error being ignored.","Run parallelsuperpmi under strace -f -e dup2 to see the failing errno and descriptor value."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Raise the fd limit before launching many parallel workers.\n#include <sys/resource.h>\nstatic bool RaiseFdLimit(rlim_t want) {\n    struct rlimit r;\n    if (getrlimit(RLIMIT_NOFILE, &r) != 0) return false;\n    if (r.rlim_cur >= want) return true;\n    r.rlim_cur = want < r.rlim_max ? want : r.rlim_max;\n    return setrlimit(RLIMIT_NOFILE, &r) == 0;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call setrlimit(RLIMIT_NOFILE) or ulimit -n before running parallelsuperpmi.","Size the worker count so (workers * pipes) fits under the fd limit.","Check open()/pipe() return values before passing descriptors to dup2.","strace -f -e dup2 to catch the failing descriptor and errno."],"tags":["superpmi","parallelsuperpmi","dup2","fork","file-descriptors","unix"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}