-
Notifications
You must be signed in to change notification settings - Fork 854
TypeScript AppHost: addCSharpAppWithOptions callback causes JSON-RPC deadlock when calling properties on callback context #15152
Description
Description
When using addCSharpAppWithOptions in a TypeScript AppHost, the callback that receives ProjectResourceOptions causes a deadlock. The callback tries to set a property (e.g., opts.launchProfileName.set("https")), which makes a nested JSON-RPC call back to the server. The server appears to be blocked waiting for the callback to complete, creating a circular dependency.
Reproduction
// This deadlocks — the apphost hangs indefinitely
const admin = await builder.addCSharpAppWithOptions("boardadmin", "./MyProject", async (opts) => {
await opts.launchProfileName.set("https"); // Nested RPC call deadlocks
});Debug Evidence
Running with aspire run --debug shows:
- The TypeScript apphost sends
addCSharpAppWithOptionsto the server (>> invokeCapability) - The server receives it and needs to invoke the callback
- Inside the callback, the TypeScript code calls
opts.launchProfileName.set("https")which sends another RPC call to the server - The response (
<<) foraddCSharpAppWithOptionsnever arrives — the server is blocked - The CLI times out after 60 seconds: "Timed out waiting for AppHost server to start"
The last server log line shows:
>> invokeCapability(Aspire.Hosting/addCSharpAppWithOptions) args: {"builder":...}
No corresponding << response is ever logged.
Analysis
The issue is that the callback execution model doesn't support re-entrant RPC calls. When the server invokes the TypeScript callback, it blocks waiting for the callback result. But the callback needs to make another RPC call to the server (to set properties on ProjectResourceOptions), which requires the server to process a new incoming request — creating a deadlock.
Contrast with withConfiguration for YARP: That method has [AspireExport("withConfiguration", RunSyncOnBackgroundThread = true)], which runs the callback on a background thread and allows re-entrant RPC processing. addCSharpAppWithOptions (or the underlying addProjectWithOptions) may lack this attribute.
Workaround
Use addProject with an explicit launch profile name instead:
const admin = await builder.addProject("boardadmin", "./MyProject", "https");However, this returns ProjectResource which lacks publishAsAzureContainerApp (only available on CSharpAppResource).
Impact
- Users cannot use
addCSharpAppWithOptionsoraddProjectWithOptionsin TypeScript AppHosts - This blocks access to
CSharpAppResource-specific methods likepublishAsAzureContainerApp - Any API that uses callbacks with property setters on the callback context may be affected
Environment
- Aspire CLI:
13.2.0-preview.1.26161.4 - OS: Windows
- Transport: Named pipes (JSON-RPC via
vscode-jsonrpc)