{"record":{"id":"814a2a09f51dbb0b","repo":"microsoft/aspire","slug":"dcp-fork-process-did-not-return-a-child-process-id-dcp-fork","errorCode":null,"errorMessage":"DCP fork-process did not return a child process ID. DCP fork-process exited with code {dcpProcess.ExitCode}. stderr: '{stderr.Trim()}'","messagePattern":"DCP fork-process did not return a child process ID\\. DCP fork-process exited with code (.+?)\\. stderr: '(.+?)'","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Processes/IsolatedProcess.Unix.cs","lineNumber":62,"sourceCode":"\n        cancellationToken.ThrowIfCancellationRequested();\n\n        var dcpProcess = Process.Start(dcpStartInfo)\n            ?? throw new InvalidOperationException(\"Failed to start DCP fork-process.\");\n\n        var stderrTask = dcpProcess.StandardError.ReadToEndAsync(CancellationToken.None);\n        var stdoutLineTask = dcpProcess.StandardOutput.ReadLineAsync(CancellationToken.None).AsTask();\n\n        try\n        {\n            // Once DCP has started, wait for it to report the detached child PID even if the caller\n            // cancels. Without the PID, callers cannot clean up a child that was already forked.\n            var stdoutLine = await stdoutLineTask.ConfigureAwait(false);\n            if (stdoutLine is null)\n            {\n                await dcpProcess.WaitForExitAsync(CancellationToken.None).ConfigureAwait(false);\n                var stderr = await stderrTask.ConfigureAwait(false);\n                throw new InvalidOperationException($\"DCP fork-process did not return a child process ID. DCP fork-process exited with code {dcpProcess.ExitCode}. stderr: '{stderr.Trim()}'\");\n            }\n\n            var trimmedStdout = stdoutLine.Trim();\n            // DCP fork-process writes only the detached child PID followed by a newline, for example:\n            //   12345\n            if (!int.TryParse(trimmedStdout, NumberStyles.None, CultureInfo.InvariantCulture, out var childPid))\n            {\n                throw new InvalidOperationException($\"DCP fork-process did not return a valid child process ID. stdout: '{trimmedStdout}'\");\n            }\n\n            ObserveDcpForkProcessStderr(stderrTask);\n\n            Process? childProcess;\n            try\n            {\n                childProcess = Process.GetProcessById(childPid);\n            }\n            catch (ArgumentException)","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Processes/IsolatedProcess.Unix.cs#L44-L80","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the stderr embedded in the exception message — it contains the actual DCP error — and fix that root cause first.","Clear stale DCP state/cache directories and retry the operation.","Update the Aspire CLI (and bundled DCP) so versions are compatible.","Run the DCP launcher manually with the same arguments/environment to reproduce its failure and inspect its output.","Check disk space, permissions, and ports if stderr indicates resource constraints."],"exampleFix":"// before: retrying blindly without reading stderr\n// after: capture and act on the embedded stderr\ntry\n{\n    var started = await isolatedProcess.StartAsync(ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"DCP fork-process\"))\n{\n    logger.LogError(ex, \"DCP launcher failed; check embedded stderr for root cause\");\n    throw;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    var started = await process.StartAsync(ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message.StartsWith(\"DCP fork-process did not return\"))\n{\n    // parse exit code and stderr from the message; fix the DCP root cause before retrying\n    logger.LogError(ex, \"DCP launcher exited without a child PID\");\n}","preventionTips":["Read the embedded stderr in the message before retrying","Keep CLI and DCP versions matched","Clear stale DCP state directories after failed runs","Ensure the app process being forked starts successfully on its own"],"tags":["process","dcp","unix","unexpected-exit"],"backgroundTag":"unexpected-response-shape","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}