{"record":{"id":"d94f8a10eab349d4","repo":"microsoft/aspire","slug":"apphost-server-process-exited-before-the-rpc-connection","errorCode":null,"errorMessage":"AppHost server process exited before the RPC connection could be established. Exit code: {code}.","messagePattern":"AppHost server process exited before the RPC connection could be established\\. Exit code: (.+?)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"src/Aspire.Cli/Projects/AppHostServerSession.cs","lineNumber":331,"sourceCode":"        var connectTask = AppHostRpcClient.ConnectAsync(socketPath, _authenticationToken, _environment, _profilingTelemetry, connectCts.Token);\n        var completedTask = await Task.WhenAny(connectTask, serverExitTask).ConfigureAwait(false);\n\n        if (completedTask == connectTask)\n        {\n            // Stop the process-exit watcher once the RPC connection wins the race.\n            connectCts.Cancel();\n            ObserveFaultedTask(serverExitTask);\n            _rpcClient = await connectTask.ConfigureAwait(false);\n            return _rpcClient;\n        }\n\n        await serverExitTask.ConfigureAwait(false);\n        // Stop the retrying connection attempt once the server has exited, then observe any\n        // cancellation/failure it reports so the losing task cannot raise an unobserved exception.\n        connectCts.Cancel();\n        ObserveFaultedTask(connectTask);\n        var exitCode = TryGetServerExitCode();\n        throw new InvalidOperationException(\n            exitCode is { } code\n                ? $\"AppHost server process exited before the RPC connection could be established. Exit code: {code}.\"\n                : \"AppHost server process exited before the RPC connection could be established.\");\n    }\n\n    public async ValueTask DisposeAsync()\n    {\n        // Acquire _startGate the same way StartAsync does so dispose is serialized against an\n        // in-flight start: we either flip _disposed before StartAsync acquires the gate (it then\n        // throws ObjectDisposedException and spawns nothing) or after it has fully published the\n        // execution + run task (we then tear that down below). The gate is released immediately;\n        // the teardown runs outside it so a late StartAsync observes _disposed and bails fast.\n        bool alreadyDisposed;\n        await _startGate.WaitAsync().ConfigureAwait(false);\n        try\n        {\n            alreadyDisposed = _disposed;\n            _disposed = true;","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Projects/AppHostServerSession.cs#L313-L349","documentation":"Thrown by AppHostServerSession.GetRpcClientAsync when the AppHost server process exits before the CLI's RPC connection attempt succeeds. The session races serverExitTask against connectTask; if the exit task wins, it cancels the connection and throws with the server's exit code when available, so the root cause (server crash) is surfaced instead of an opaque connect timeout.","triggerScenarios":"Calling GetRpcClientAsync when the spawned server process terminates (crash, bad args, port/env problems) while ConnectToServerAsync is still retrying.","commonSituations":"AppHost project fails to build at startup, missing SDK or runtime on the machine, invalid command-line arguments passed to the server, unhandled exception in server startup, or the process killed by the OS.","solutions":["Inspect the server process stdout/stderr captured by the session for the startup error","Fix the AppHost build/runtime error causing the exit (missing SDK, compile error)","Verify the server launch arguments and environment (DOTNET_ROOT etc.) are correct","Check the exit code reported in the message and correlate with server logs"],"exampleFix":"// before\nvar session = await AppHostServerSession.CreateAsync(badProjectPath); // exits code 1\n// after\ndotnet build MyApp.AppHost.csproj // fix compile errors first\nvar session = await AppHostServerSession.CreateAsync(projectPath);","handlingStrategy":"try-catch","validationCode":"// Confirm the AppHost builds before launching the server session\nvar buildResult = await Process.RunAsync(\"dotnet\", $\"build {appHostProjectPath}\");\nif (buildResult.ExitCode != 0)\n    throw new InvalidOperationException(\"AppHost build failed; fix before starting a session.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var rpc = await session.GetRpcClientAsync(ct);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"exited before\"))\n{\n    var log = session.CapturedServerOutput;\n    logger.LogError(ex, \"Server exited early. Output: {Output}\", log);\n}","preventionTips":["Capture and inspect the server process's stdout/stderr on every launch","Fix AppHost build errors before starting sessions","Verify DOTNET_ROOT and required SDKs are available to the child process","Record the exit code from the message to guide troubleshooting"],"tags":["process-exit","rpc","startup"],"backgroundTag":"process-exited-unexpectedly","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}