microsoft/aspire · error · InvalidOperationException

DCP fork-process did not return a child process ID. DCP…

Error message

DCP fork-process did not return a child process ID. DCP fork-process exited with code {dcpProcess.ExitCode}. stderr: '{stderr.Trim()}'

What it means

The DCP fork-process contract is to print exactly the detached child's PID on stdout followed by a newline. When stdout ends without a line (stream closed), the launcher exited without producing a child PID, so StartDetachedUnixAsync waits for exit and throws with the exit code and captured stderr to expose the underlying DCP failure. Without a PID the caller cannot manage the detached child.

Solutions

  1. Read the stderr embedded in the exception message — it contains the actual DCP error — and fix that root cause first.
  2. Clear stale DCP state/cache directories and retry the operation.
  3. Update the Aspire CLI (and bundled DCP) so versions are compatible.
  4. Run the DCP launcher manually with the same arguments/environment to reproduce its failure and inspect its output.
  5. Check disk space, permissions, and ports if stderr indicates resource constraints.

Example fix

// before: retrying blindly without reading stderr
// after: capture and act on the embedded stderr
try
{
    var started = await isolatedProcess.StartAsync(ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("DCP fork-process"))
{
    logger.LogError(ex, "DCP launcher failed; check embedded stderr for root cause");
    throw;
}
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    var started = await process.StartAsync(ct);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("DCP fork-process did not return"))
{
    // parse exit code and stderr from the message; fix the DCP root cause before retrying
    logger.LogError(ex, "DCP launcher exited without a child PID");
}

Prevention

When it happens

Trigger: During StartAsync detached Unix launch, the DCP launcher process exits (any non-zero or even zero without printing a PID) before writing a PID line, e.g. DCP config errors, port/resource failures, or DCP crash on startup.

Common situations: Corrupt or stale DCP state directory; DCP failing to bind its socket; incompatible DCP version with the CLI; the inner app process failing immediately so the launcher aborts.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/814a2a09f51dbb0b. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Cli/Processes/IsolatedProcess.Unix.cs:62

        cancellationToken.ThrowIfCancellationRequested();

        var dcpProcess = Process.Start(dcpStartInfo)
            ?? throw new InvalidOperationException("Failed to start DCP fork-process.");

        var stderrTask = dcpProcess.StandardError.ReadToEndAsync(CancellationToken.None);
        var stdoutLineTask = dcpProcess.StandardOutput.ReadLineAsync(CancellationToken.None).AsTask();

        try
        {
            // Once DCP has started, wait for it to report the detached child PID even if the caller
            // cancels. Without the PID, callers cannot clean up a child that was already forked.
            var stdoutLine = await stdoutLineTask.ConfigureAwait(false);
            if (stdoutLine is null)
            {
                await dcpProcess.WaitForExitAsync(CancellationToken.None).ConfigureAwait(false);
                var stderr = await stderrTask.ConfigureAwait(false);
                throw new InvalidOperationException($"DCP fork-process did not return a child process ID. DCP fork-process exited with code {dcpProcess.ExitCode}. stderr: '{stderr.Trim()}'");
            }

            var trimmedStdout = stdoutLine.Trim();
            // DCP fork-process writes only the detached child PID followed by a newline, for example:
            //   12345
            if (!int.TryParse(trimmedStdout, NumberStyles.None, CultureInfo.InvariantCulture, out var childPid))
            {
                throw new InvalidOperationException($"DCP fork-process did not return a valid child process ID. stdout: '{trimmedStdout}'");
            }

            ObserveDcpForkProcessStderr(stderrTask);

            Process? childProcess;
            try
            {
                childProcess = Process.GetProcessById(childPid);
            }
            catch (ArgumentException)

View on GitHub (pinned to 25830f84bd)