dotnet/runtime · error

dup2(stderr) failed: %s\n

Error message

dup2(stderr) failed: %s\n

What it means

Symmetrical to the stdout case: the forked ParallelSuperPMI child redirects stderr to a captured pipe with dup2(hStdError,STDERR_FILENO). A -1 return prints this message with strerror(errno) and the child _exit(1)s. It is the second redirection, attempted only after stdout dup2 succeeded.

Source

Thrown at src/coreclr/tools/superpmi/superpmi/parallelsuperpmi.cpp:220

    {
        LogError("fork() failed: %s", strerror(errno));
        for (int i = 0; i < argc; i++)
            delete[] argv[i];
        delete[] argv;
        return false;
    }

    if (*pid == 0)
    {
        // Child process: redirect stdout and stderr then exec.
        if (dup2(hStdOutput, STDOUT_FILENO) == -1)
        {
            fprintf(stderr, "dup2(stdout) failed: %s\n", strerror(errno));
            _exit(1);
        }
        if (dup2(hStdError, STDERR_FILENO) == -1)
        {
            fprintf(stderr, "dup2(stderr) failed: %s\n", strerror(errno));
            _exit(1);
        }

        // Close the original fds now that they are duplicated to STDOUT/STDERR.
        // The O_CLOEXEC on the parent's open() handles the sibling worker fds.
        if (hStdOutput != STDOUT_FILENO)
            close(hStdOutput);
        if (hStdError != STDERR_FILENO)
            close(hStdError);

        execv(argv[0], argv);
        // If execv returns, it failed.
        fprintf(stderr, "execv(%s) failed: %s\n", argv[0], strerror(errno));
        _exit(1);
    }

    // Parent process: free argv and return.
    for (int i = 0; i < argc; i++)

View on GitHub (pinned to 60108ba66e)

Solutions

  1. Increase ulimit -n so the combined stdout+stderr pipes for all workers fit.
  2. Lower the worker count (-j/--workers).
  3. Verify the code that opens hStdError surfaces open() failures rather than passing INVALID_HANDLE_VALUE into dup2.
  4. strace -f -e dup2 to capture the exact errno and descriptor.
Defensive patterns

Strategy: validation

Validate before calling

// Same fd-limit discipline as stdout; both pipes must fit.
#include <sys/resource.h>
static bool RaiseFdLimit(rlim_t want) {
    struct rlimit r;
    if (getrlimit(RLIMIT_NOFILE, &r) != 0) return false;
    if (r.rlim_cur >= want) return true;
    r.rlim_cur = want < r.rlim_max ? want : r.rlim_max;
    return setrlimit(RLIMIT_NOFILE, &r) == 0;
}

Prevention

When it happens

Trigger: hStdError is not a valid open descriptor or the process is out of file descriptors (EMFILE); the stderr pipe handle was closed or invalidated before the child runs dup2. Child-side fatal during worker startup.

Common situations: Per-process fd exhaustion when spawning many workers (each worker holds stdout+stderr pipes); a race closing the stderr pipe before fork completes; an upstream open() failure for the stderr pipe that was not checked.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/6b0a443dd45f602c. Report an issue: GitHub.