microsoft/aspire · error · InvalidOperationException
DCP fork-process did not return a valid child process ID…
Error message
DCP fork-process did not return a valid child process ID. stdout: '{trimmedStdout}' What it means
Aspire CLI launches detached child processes on Unix by invoking `dcp fork-process --monitor <pid> ...`, which must print exactly one line containing the detached child's PID. When that line cannot be parsed as a plain non-negative integer, the CLI throws this InvalidOperationException because it cannot obtain a process handle or perform any cleanup without a PID. The raw stdout is included in the message so you can see what DCP actually printed instead.
Solutions
- Inspect the stdout text in the message to identify what DCP printed instead of the PID (banner, warning, error).
- Ensure the DCP binary used (DetachedUnixLauncherPath / ASPIRE_DCP path config) matches the CLI version; delete stale ~/.aspire/bin/dcp copies and let the CLI re-acquire DCP.
- Clear environment variables that could make dcp log to stdout (e.g. DOTNET_* logging verbosity overrides like COREHOST_TRACE or ASPNETCORE_logging).
- Update Aspire CLI to the latest version so the fork-process output contract matches your DCP build.
- If the issue persists, capture DCP's stderr (observed via ObserveDcpForkProcessStderr) and file an issue with both streams.
Example fix
// before: DCP emits a banner line before the PID // Aspire DCP v1.2.3 // 12345 // after: pin a matching DCP version / remove the banner-producing wrapper so the first stdout line is: // 12345
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the DCP launcher path exists and matches the CLI before starting
if (!File.Exists(startInfo.DetachedUnixLauncherPath))
{
throw new InvalidOperationException($"DCP launcher not found at '{startInfo.DetachedUnixLauncherPath}'; update the Aspire CLI.");
} Try / catch
try
{
var process = await isolatedProcess.StartAsync(...);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("did not return a valid child process ID"))
{
// log ex.Message (contains raw DCP stdout) and re-acquire/update DCP before retrying
} Prevention
- Keep Aspire CLI and its DCP binary at matching versions; never hand-swap the dcp binary.
- Avoid setting stdout-writing env overrides (COREHOST_TRACE, verbose logging) for CLI-spawned processes.
- Don't wrap the dcp executable in scripts that print banners to stdout.
- When upgrading Aspire, delete stale ~/.aspire/bin DCP caches so the CLI re-provisions.
When it happens
Trigger: StartDetachedUnixAsync (called via StartAsync) reads one line from the DCP fork-process stdout and gets a line that fails int.TryParse under NumberStyles.None — e.g. an empty/whitespace line, a version banner, a warning or log message emitted before/instead of the PID, a localized message, or a negative number.
Common situations: A mismatched or corrupted DCP binary on PATH (different Aspire CLI/DCP version that prints extra output), an outdated Aspire CLI after a DCP protocol change, DCP emitting deprecation or environment warnings to stdout, or a wrapper script around dcp that adds output.
Related errors
- DCP fork-process did not return a child process ID. DCP…
- Failed to start DCP fork-process.
- Unix detached process launch requires a DCP executable path.
- Could not determine the identity timestamp for monitor…
- Could not find DCP executable in the Aspire layout.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/f8d750fdcb310619.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Processes/IsolatedProcess.Unix.cs:70
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)
{
// A short-lived detached child can exit and be reaped by the DCP monitor between
// DCP printing its PID and this parent opening a Process handle. In that case the
// monitor process is the only remaining handle that can report the child's exit code.
await dcpProcess.WaitForExitAsync(CancellationToken.None).ConfigureAwait(false);
return new StartedProcess(
dcpProcess,
TextReader.Null,View on GitHub (pinned to 25830f84bd)